How to reload libgdx unmanaged Texture after OpenGL context loss - android

I'm downloading images over the network and add them to my libgdx UI as Image actors using this:
Pixmap pm = new Pixmap(data, 0, data.length);
Texture t = new Texture(pm);
TextureRegion tr = new TextureRegion(t,200,300);
TextureRegionDrawable trd = new TextureRegionDrawable(tr);
Image icon = new Image();
icon.setDrawable(trd);
Given this I need some way of reloading the texture data since it is lost when the OpenGL context is lost (e.g. because the screen goes to sleep).
I've tried making my own manager class, adding
DynamicTextureManager.register(t, pm); // Register texture together with the source pixmap
to the above snippet, and in resume() I do:
DynamicTextureManager.reload();
The manager class:
public class DynamicTextureManager {
private static LinkedHashMap<Texture, Pixmap> theMap = new
LinkedHashMap<Texture,Pixmap>();
public static void reload() {
Set<Entry<Texture,Pixmap>> es = theMap.entrySet();
for(Entry<Texture,Pixmap> e : es) {
Texture t = e.getKey();
Pixmap p = e.getValue();
t.draw(p, 0, 0);
}
}
public static void register(Texture t, Pixmap p) {
theMap.put(t, p);
}
}
But this doesn't help - I still end up with the texture being unloaded and white areas instead of the image.
How should this be done? I haven't been able to find any code demonstrating this!

Adding my solution as a reference. I now register the Image object and the Pixmap object with my manager, on reload() the Texture is re-created from the Pixmap and I set the new Texture for the old Image. Works for me, but more elegant solutions are welcome.
import java.util.Map.Entry;
public class DynamicTextureManager {
private static final class MapData {
Pixmap pixmap;
int width;
int height;
}
private static WeakHashMap<Image, MapData> theMap = new WeakHashMap<Image, MapData>();
public static void reload() {
Set<Entry<Image, MapData>> es = theMap.entrySet();
for (Entry<Image, MapData> e : es) {
Image i = e.getKey();
MapData d = e.getValue();
Texture t = new Texture(d.pixmap);
TextureRegion tr;
if(d.width == -1 || d.height == -1) {
tr = new TextureRegion(t);
}
else {
tr = new TextureRegion(t,d.width, d.height);
}
TextureRegionDrawable trd = new TextureRegionDrawable(tr);
i.setDrawable(trd);
}
}
public static void register(Image i, Pixmap p) {
MapData d = new MapData();
d.pixmap = p;
d.width = -1;
d.height = -1;
theMap.put(i, d);
}
public static void register(Image i, Pixmap p, int width, int height) {
MapData d = new MapData();
d.pixmap = p;
d.width = width;
d.height = height;
theMap.put(i, d);
}
}

Related

Libgdx AssetsManager as static variable

I am trying to implement the AssetsManager to my app. It should only load all the font files and a background music. All this works perfectly. My problem is that sometimes on android the AssetsManger files are away/wrong/buggy when I leave the App and rejoin it later on. I know this is because the life time of the assets is different on Android as the static Variable, thats why this issues occure. My question is how to fix this? The AssetsManager HAS to be static, otherwise I am not able to get the assets from an other class.
My code so far (how to improve?):
public class Colors extends ApplicationAdapter {
//MAIN
private GameStateManager gsm;
private SpriteBatch sb;
private boolean tomenu = false;
//STATIC
public static int WIDTH = 480;
public static int HEIGHT = 800;
//Generell
private FreeTypeFontGenerator fontGenerator;
private FreeTypeFontGenerator.FreeTypeFontParameter fontParameter;
private BitmapFont font;
private Texture loaderb;
private Texture loader;
private Texture timelablogo;
//Asset Manager
public static AssetManager manager = new AssetManager();
#Override
public void create () {
Gdx.gl.glClearColor(71/255f, 92/255f, 141/255f, 1);
WIDTH = Gdx.graphics.getWidth();
HEIGHT = Gdx.graphics.getHeight();
Gdx.input.setCatchBackKey(true); //Backkey is catched and does not close the App
gsm = new GameStateManager();
sb = new SpriteBatch();
font = new BitmapFont();
loaderb = new Texture("Buttons/loadingbackground.png");
loader = new Texture("Buttons/loader.png");
timelablogo = new Texture("timelab.png");
//ASSETMANAGER (alle assets[texturen,schirftne,sounds,..])
manager.load("Sound/menubackground.ogg", Music.class);
//FreetypeFontGenerator zum AssetManager hinzufügen
FileHandleResolver resolver = new InternalFileHandleResolver();
manager.setLoader(FreeTypeFontGenerator.class, new FreeTypeFontGeneratorLoader(resolver));
manager.setLoader(BitmapFont.class, ".ttf", new FreetypeFontLoader(resolver));
//Create Fonts and give them to the AssetManager
FreetypeFontLoader.FreeTypeFontLoaderParameter params1 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
params1.fontFileName = "Fonts/kenvector_future1.ttf";
params1.fontParameters.size = WIDTH/7;
params1.fontParameters.color = Color.DARK_GRAY;
manager.load("Fonts/kenvector_future1.ttf", BitmapFont.class, params1);
FreetypeFontLoader.FreeTypeFontLoaderParameter params2 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
params2.fontFileName = "Fonts/kenvector_future2.ttf";
params2.fontParameters.size = WIDTH/25;
params2.fontParameters.color = Color.DARK_GRAY;
manager.load("Fonts/kenvector_future2.ttf", BitmapFont.class, params2);
FreetypeFontLoader.FreeTypeFontLoaderParameter params3 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
params3.fontFileName = "Fonts/kenvector_future3.ttf";
params3.fontParameters.size = WIDTH/15;
params3.fontParameters.color = Color.DARK_GRAY;
manager.load("Fonts/kenvector_future3.ttf", BitmapFont.class, params3);
FreetypeFontLoader.FreeTypeFontLoaderParameter params4 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
params4.fontFileName = "Fonts/kenvector_future4.ttf";
params4.fontParameters.size = WIDTH/12;
params4.fontParameters.color = Color.DARK_GRAY;
manager.load("Fonts/kenvector_future4.ttf", BitmapFont.class, params4);
FreetypeFontLoader.FreeTypeFontLoaderParameter params5 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
params5.fontFileName = "Fonts/kenvector_future5.ttf";
params5.fontParameters.size = WIDTH/18;
params5.fontParameters.color = Color.DARK_GRAY;
manager.load("Fonts/kenvector_future5.ttf", BitmapFont.class, params5);
FreetypeFontLoader.FreeTypeFontLoaderParameter params6 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
params6.fontFileName = "Fonts/kenvector_future6.ttf";
params6.fontParameters.size = WIDTH/15;
params6.fontParameters.color.set(99/255f, 99/255f, 99/255f, 1);
manager.load("Fonts/kenvector_future6.ttf", BitmapFont.class, params6);
}
#Override
public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if(manager.update()) {
if(!tomenu) {
tomenu = true;
manager.get("Sound/menubackground.ogg",Music.class).setLooping(true);
manager.get("Sound/menubackground.ogg",Music.class).setVolume(0.7f);
manager.get("Sound/menubackground.ogg",Music.class).play();
//When everything is loaded a new State is pushed, which uses the font assets from this Assetsmanger
gsm.push(new MenuStateNew(gsm,0,0,false,1));
}
gsm.update(Gdx.graphics.getDeltaTime());
gsm.render(sb);
sb.begin();
font.draw(sb,WIDTH+"px*"+HEIGHT+"px closed BETA v11 "+String.valueOf((int)(1/Gdx.graphics.getDeltaTime()))+" FPS",5,20);
sb.end();
} else {
sb.begin();
font.draw(sb,WIDTH+"px*"+HEIGHT+"px closed BETA v11 "+String.valueOf((int)(1/Gdx.graphics.getDeltaTime()))+" FPS",5,20);
sb.draw(loaderb,0,0,WIDTH,WIDTH*0.25f);
sb.draw(loader,0,0,WIDTH*manager.getProgress(),WIDTH*0.25f);
sb.draw(timelablogo,WIDTH/2-WIDTH/6,HEIGHT/2-WIDTH/6,WIDTH/3,WIDTH/3);
sb.end();
}
}
#Override
public void pause() {
gsm.pause();
}
#Override
public void dispose () {
sb.dispose();
manager.dispose();
font.dispose();
}
}
EDIT 1:
I implemented the code as you sayed but know the whole game is more dark (no erros)
My code now looks like this (and causes this darker screen :/)
public class Colors extends ApplicationAdapter {
//MAIN
private GameStateManager gsm;
private SpriteBatch sb;
private boolean tomenu = false;
//STATIC
public static int WIDTH = 480;
public static int HEIGHT = 800;
//Generell
private BitmapFont font;
private Texture loaderb;
private Texture loader;
private Texture timelablogo;
//Asset Manager
private Assets assets;
#Override
public void create () {
Gdx.gl.glClearColor(71/255f, 92/255f, 141/255f, 1);
WIDTH = Gdx.graphics.getWidth();
HEIGHT = Gdx.graphics.getHeight();
//Assetmanager
assets = new Assets();
getAssets().load(); //starts loading assets
Gdx.input.setCatchBackKey(true); //Backkey is catched and does not close the App
gsm = new GameStateManager();
sb = new SpriteBatch();
font = new BitmapFont();
loaderb = new Texture("Buttons/loadingbackground.png");
loader = new Texture("Buttons/loader.png");
timelablogo = new Texture("timelab.png");
}
public static Assets getAssets() {
return ((Colors) Gdx.app.getApplicationListener()).assets;
}
#Override
public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if(getAssets().manager.update()) {
if(!tomenu) {
tomenu = true;
getAssets().manager.get("Sound/menubackground.ogg",Music.class).setLooping(true);
getAssets().manager.get("Sound/menubackground.ogg",Music.class).setVolume(0.7f);
getAssets().manager.get("Sound/menubackground.ogg",Music.class).play();
gsm.push(new MenuStateNew(gsm,0,0,false,1));
}
gsm.update(Gdx.graphics.getDeltaTime());
gsm.render(sb);
sb.begin();
font.draw(sb,WIDTH+"px*"+HEIGHT+"px closed BETA v12 "+String.valueOf((int)(1/Gdx.graphics.getDeltaTime()))+" FPS",5,20);
sb.end();
} else {
sb.begin();
font.draw(sb,WIDTH+"px*"+HEIGHT+"px closed BETA v12 "+String.valueOf((int)(1/Gdx.graphics.getDeltaTime()))+" FPS",5,20);
sb.draw(loaderb,0,0,WIDTH,WIDTH*0.25f);
sb.draw(loader,0,0,WIDTH*getAssets().manager.getProgress(),WIDTH*0.25f);
sb.draw(timelablogo,WIDTH/2-WIDTH/6,HEIGHT/2-WIDTH/6,WIDTH/3,WIDTH/3);
sb.end();
}
}
#Override
public void pause() {
gsm.pause();
}
#Override
public void resume() { assets.manager.finishLoading(); }
#Override
public void dispose () {
sb.dispose();
assets.dispose();
font.dispose();
}
}
And the Assets Class:
public class Assets implements Disposable {
public AssetManager manager = new AssetManager();
public void load() {
loadMusics();
loadSounds();
loadFonts();
}
private void loadSounds() {
manager.load("Sound/menubackground.ogg", Music.class);
}
private void loadMusics() {
manager.load("Sound/menubackground.ogg", Music.class);
}
private void loadFonts() {
//FreetypeFontGenerator zum AssetManager hinzufügen
FileHandleResolver resolver = new InternalFileHandleResolver();
manager.setLoader(FreeTypeFontGenerator.class, new FreeTypeFontGeneratorLoader(resolver));
manager.setLoader(BitmapFont.class, ".ttf", new FreetypeFontLoader(resolver));
//Create Fonts and give them to the AssetManager
FreetypeFontLoader.FreeTypeFontLoaderParameter params1 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
params1.fontFileName = "Fonts/kenvector_future1.ttf";
params1.fontParameters.size = WIDTH/7;
params1.fontParameters.color = Color.DARK_GRAY;
manager.load("Fonts/kenvector_future1.ttf", BitmapFont.class, params1);
FreetypeFontLoader.FreeTypeFontLoaderParameter params2 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
params2.fontFileName = "Fonts/kenvector_future2.ttf";
params2.fontParameters.size = WIDTH/25;
params2.fontParameters.color = Color.DARK_GRAY;
manager.load("Fonts/kenvector_future2.ttf", BitmapFont.class, params2);
FreetypeFontLoader.FreeTypeFontLoaderParameter params3 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
params3.fontFileName = "Fonts/kenvector_future3.ttf";
params3.fontParameters.size = WIDTH/15;
params3.fontParameters.color = Color.DARK_GRAY;
manager.load("Fonts/kenvector_future3.ttf", BitmapFont.class, params3);
FreetypeFontLoader.FreeTypeFontLoaderParameter params4 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
params4.fontFileName = "Fonts/kenvector_future4.ttf";
params4.fontParameters.size = WIDTH/12;
params4.fontParameters.color = Color.DARK_GRAY;
manager.load("Fonts/kenvector_future4.ttf", BitmapFont.class, params4);
FreetypeFontLoader.FreeTypeFontLoaderParameter params5 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
params5.fontFileName = "Fonts/kenvector_future5.ttf";
params5.fontParameters.size = WIDTH/18;
params5.fontParameters.color = Color.DARK_GRAY;
manager.load("Fonts/kenvector_future5.ttf", BitmapFont.class, params5);
FreetypeFontLoader.FreeTypeFontLoaderParameter params6 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
params6.fontFileName = "Fonts/kenvector_future6.ttf";
params6.fontParameters.size = WIDTH/15;
params6.fontParameters.color.set(99/255f, 99/255f, 99/255f, 1);
manager.load("Fonts/kenvector_future6.ttf", BitmapFont.class, params6);
}
#Override
public void dispose() {
manager.dispose();
}
}
MenuStateNew Class (only an except):
public class MenuStateNew extends State implements GestureDetector.GestureListener {
//Generel
private int width = Colors.WIDTH;
private int height = Colors.HEIGHT;
private GestureDetector gsdt;
private float zeit;
private int mode;
//Buttons
private Texture btn_settings;
private Texture btn_settingsh;
private Sprite sp_settings;
private Rectangle ra_settings;
private Texture btn_easy;
private Texture btn_easyh;
private Texture btn_hard;
private Texture btn_hardh;
private Sprite sp_easy;
private Rectangle ra_easy;
private Texture btn_start;
private Texture btn_starth;
private Sprite sp_start;
private Rectangle ra_start;
private Texture btn_list;
private Rectangle ra_list;
//Fonts
private BitmapFont menufont = Colors.getAssets().manager.get("Fonts/kenvector_future6.ttf", BitmapFont.class);
//Background
private Array<Backgrounddot> backgrounddots;
//Sounds
private Sound s_click;
//Particle Effects
private ParticleEffect pe1;
private ParticleEffect pe2;
//Speicher
Preferences prefs;
public MenuStateNew(GameStateManager pgsm,int plastscore,int plastlevel,boolean highscore,int pmode) {
super(pgsm);
//Generell
gsdt = new GestureDetector(this);
Gdx.input.setInputProcessor(gsdt);
mode = pmode;//1: easy,0: hard
}
#Override
public void render(SpriteBatch sb) {
sb.begin();
//Particles
pe1.draw(sb);
pe2.draw(sb);
//BackgroundDots
for(Backgrounddot bgd : backgrounddots) {
sb.draw(bgd.getTxt_dot(),bgd.getDotposition().x,bgd.getDotposition().y,bgd.getDotwidth(),bgd.getDotwidth());
}
sb.draw(sp_settings,ra_settings.getX(),height-ra_settings.getY()-ra_settings.getHeight(),ra_settings.getWidth(),ra_settings.getHeight());
sb.draw(btn_list,ra_list.getX(),height-ra_list.getY()-ra_list.getHeight(),ra_list.getWidth(),ra_list.getHeight());
sb.draw(sp_easy,ra_easy.getX(),height-ra_easy.getY()-ra_easy.getHeight(),ra_easy.getWidth(),ra_easy.getHeight());
sb.draw(sp_start,ra_start.getX(),height-ra_start.getY()-ra_start.getHeight(),ra_start.getWidth(),ra_start.getHeight());
if(mode == 1) {
menufont.draw(sb,"Last: "+prefs.getInteger("lastscore2")+" ("+prefs.getInteger("lastlevel2")+")",width/6,height*0.63f);
menufont.draw(sb,"Best: "+prefs.getInteger("highscore2"),width/6,height*0.515f);
menufont.draw(sb,"Level "+prefs.getInteger("level2"),width/6,height*0.4f);
} else if(mode == 0) {
menufont.draw(sb,"Last: "+prefs.getInteger("lastscore")+" ("+prefs.getInteger("lastlevel")+")",width/6,height*0.63f);
menufont.draw(sb,"Best: "+prefs.getInteger("highscore"),width/6,height*0.515f);
menufont.draw(sb,"Level "+prefs.getInteger("level"),width/6,height*0.4f);
}
sb.end();
}
Did I somethign wrong?
I will expand on Abhishek's answer by providing a workaround for having to pass an Assets class around to all of your game objects.
This approach is most certainly not the 'best design', but it is convenient, and safe (to the best of my knowledge).
Follow Abhishek's steps in creating an Assets class, and from there you can add the following function in your GdxGame class (the base class)
public static Assets getAssets() {
return ((GdxGame) Gdx.app.getApplicationListener()).assets;
}
Now, whenever you need an texture or some asset, you can call GdxGame.getAssets().getTextureRegion(...). This way you can actually access the assets on a static basis (convenient) and still have the asset manager as an instance variable (safe).
PS. Am on a mobile device, so, apologies for any poor formatting.
You should make a class where you store your assets.
public class Assets implements Disposable {
public AssetManager manager = new AssetManager();
public void load() {
manager.load("data/mytexture.png", Texture.class);
manager.load("data/myfont.fnt", BitmapFont.class);
manager.load("data/mymusic.ogg", Music.class);
}
#Override
public void dispose() {
manager.dispose();
}
}
Keep reference of Your Assets object in Game(Main) class and pass it as reference where it needed.
public class Colors extends ApplicationAdapter {
Assets assets;
#Override
public void create() {
assets = new Assets();
assets.load(); //starts loading assets
assets.manager.finishLoading(); //Continues when done loading.
//it won't continue until all assets are finished loading.
}
#Override
public void dispose() {
assets.dispose();
}
}
EDIT
Get or initialize all your Private data member(resources) in constructor and then use in render method.
private BitmapFont menufont;
public MenuStateNew(GameStateManager pgsm,int plastscore,int plastlevel,boolean highscore,int pmode) {
super(pgsm);
//Generell
gsdt = new GestureDetector(this);
Gdx.input.setInputProcessor(gsdt);
mode = pmode;//1: easy,0: hard
menufont = Colors.getAssets().manager.get("Fonts/kenvector_future6.ttf", BitmapFont.class);
}
This is just one example for menufont, same like you need to initialize all required resources in MenuStateNew in his constructor.

how to add the "int" (Array of drawables) in an array of scene

Mat last;
//Mat last2;
ArrayList<Scene> scenes = new ArrayList<Scene>();
//ArrayList<Scene> scenes23 = new ArrayList<Scene>();
ArrayList<Bitmap> myImageList2 = new ArrayList<Bitmap>();
int[] myImageList = new int[]{R.drawable.baldglassy,
R.drawable.baldglassy2,
R.drawable.bandedarcherfish,
R.drawable.bandedarcherfish2,
R.drawable.bluegill,
R.drawable.bluegill2,
R.drawable.bluespotmullet};
/*Bitmap[] images2 = { BitmapFactory.decodeResource(getResources(),R.drawable.baldglassy),
BitmapFactory.decodeResource(getResources(),R.drawable.baldglassy2),
BitmapFactory.decodeResource(getResources(),R.drawable.bandedarcherfish),
BitmapFactory.decodeResource(getResources(),R.drawable.bluegill),
BitmapFactory.decodeResource(getResources(),R.drawable.bluegill2),
BitmapFactory.decodeResource(getResources(),R.drawable.bluespotmullet),
};*/
Scene refScene;
ProgressDialog progress;
//Mat imgMAT;
public void takePic1(View w) {
//Bitmap bmp32 = images[](Bitmap.Config.ARGB_8888, true);
/*for(int i=0;i<=5;i++){
Bitmap bmp32 = images[i].copy(Bitmap.Config.ARGB_8888, true);
Utils.bitmapToMat(bmp32, imgMAT);
}
Scene scene2 = new Scene(imgMAT);
scenes.add(scene2);*/
Scene scene = new Scene(last);
scenes.add(scene);
addBtn.setText("Add (" + scenes.size() + ")");
}
public void takePic2(View w) {
Mat im = last.clone();
// Imgproc.cvtColor(im, im, Imgproc.COLOR_BGR2RGB);
Bitmap bmp = Bitmap.createBitmap(im.cols(), im.rows(),
Bitmap.Config.ARGB_8888);
Utils.matToBitmap(im, bmp);
matchDrawArea.setImageBitmap(bmp);
refScene = new Scene(last);
}
when i use a bitmap array, the app crashes
it says that change the type of my int array to Scene. But it is impossible. Thanks in advance to those who will help.
here is the scene class
public class Scene {
final Mat image;
final Mat descriptors = new Mat();
final MatOfKeyPoint keypoints = new MatOfKeyPoint();
boolean firstTime = true;
public Scene(Mat image) {
this.image = image.clone();
// DetectUtility.analyze(image, keypoints, descriptors);
}
public void preCompute() {
if (firstTime) {
DetectUtility.analyze(image, keypoints, descriptors);
firstTime = false;
}
}
public SceneDetectData compare(Scene frame, boolean isHomogrpahy, boolean imageOnly) {
// Info to store analysis stats
SceneDetectData s = new SceneDetectData();
// Detect key points and compute descriptors for inputFrame
MatOfKeyPoint f_keypoints = frame.keypoints;
Mat f_descriptors = frame.descriptors;
this.preCompute();
frame.preCompute();
// Compute matches
MatOfDMatch matches = DetectUtility.match(descriptors, f_descriptors);
// Filter matches by distance
MatOfDMatch filtered = DetectUtility.filterMatchesByDistance(matches);
// If count of matches is OK, apply homography check
s.original_key1 = (int) descriptors.size().height;
s.original_key2 = (int) f_descriptors.size().height;
s.original_matches = (int) matches.size().height;
s.dist_matches = (int) filtered.size().height;
if (isHomogrpahy) {
MatOfDMatch homo = DetectUtility.filterMatchesByHomography(
keypoints, f_keypoints, filtered);
Bitmap bmp = DetectUtility.drawMatches(image, keypoints,
frame.image, f_keypoints, homo, imageOnly);
s.bmp = bmp;
s.homo_matches = (int) homo.size().height;
return s;
} else {
Bitmap bmp = DetectUtility.drawMatches(image, keypoints,
frame.image, f_keypoints, filtered, imageOnly);
s.bmp = bmp;
s.homo_matches = -1;
return s;
}
}
}

animated sprite from multiple sprite sheets

Im new to android dev and using andengine and ive just come across a problem when dealing with a large animation that covers more then 1 sprite sheet. Basically I have a large sprite whose animation runs across 2 sprite sheets. Im trying to find a way to load them successfully. I will show you what I am trying and hopefully some one can either show me the correct way or help me finish it my way.
i start off by creating the 2 texture packs from the xml files.
these are created fine
TexturePackTextureRegionLibrary packer1 = null,packer2 = null;
TexturePack spritesheetTexturePack1 = null,spritesheetTexturePack2 = null;
try {
spritesheetTexturePack1 = new TexturePackLoader(activity.getTextureManager(), "Animation/Jack/")
.loadFromAsset(activity.getAssets(), "Jack_walk1" + ".xml");
spritesheetTexturePack1.loadTexture();
packer1 = spritesheetTexturePack1.getTexturePackTextureRegionLibrary();
} catch (final TexturePackParseException e) {
Debug.e(e);
}
TexturePackerTextureRegion textureRegion = packer1.get(Jack_walk1.LOOP_JACK_WALK_TO_SAFE_AREA_00000_ID);
try {
spritesheetTexturePack2 = new TexturePackLoader(activity.getTextureManager(), "Animation/Jack/")
.loadFromAsset(activity.getAssets(), "Jack_walk2" + ".xml");
spritesheetTexturePack2.loadTexture();
packer2 = spritesheetTexturePack2.getTexturePackTextureRegionLibrary();
} catch (final TexturePackParseException e) {
Debug.e(e);
}
TexturePackerTextureRegion textureRegion2 = packer2.get(Jack_walk1.LOOP_JACK_WALK_TO_SAFE_AREA_00000_ID);
ArrayList<SparseArray> animList = new ArrayList<SparseArray>();
animList.add(packer1.getIDMapping());
animList.add(packer2.getIDMapping());
TiledTextureRegion text1 = TiledTextureRegion.create(textureRegion.getTexture(), (int) textureRegion.getTextureX(), (int) textureRegion.getTextureY(), animList);
I then added this function to the tiledtextureregion to take in a list of arrays that hold the frame information and step through adding them to the itexturregion array
public static TiledTextureRegion create(final ITexture pTexture, final int pTextureX, final int pTextureY, final ArrayList<SparseArray> animList) {
ITextureRegion[] textureRegions = null;
int maxFrame = 0;
for(int i = 0; i < animList.size(); i++){
maxFrame += animList.get(i).size();
}
int currentFrame = 0;
textureRegions = new ITextureRegion[maxFrame];
for(int i = 0 ; i < animList.size(); i++){
SparseArray<? extends ITexturePackTextureRegion> packer = animList.get(i);
for(int j = 0; j < packer.size(); j++) {
if (packer.valueAt(j)!= null){
final int x = (int) packer.valueAt(j).getTextureX();
final int y = (int) packer.valueAt(j).getTextureY();
final int width = packer.valueAt(j).getSourceWidth();
final int height = packer.valueAt(j).getSourceHeight();
final Boolean rotated = packer.valueAt(j).isRotated();
textureRegions[currentFrame] = new TextureRegion(pTexture, x, y, width, height, rotated);
currentFrame++;
}
}
}
return new TiledTextureRegion(pTexture, false, textureRegions);
}
but the line return new TiledTextureRegion(pTexture, false, textureRegions); is expecting 1 texture do retrieve the frames from when creating the tiled region. Any ideas where i should go from here or is there a super easy way to handle this that i have over looked. Thanks for any help
This class is designed to work with a single texture.
If you cannot merge your textures into one (which I think is the case), then you can try to write a new class implementing ITextureRegion that will contain 2 or more TiledTextureRegion objects, and which will have a method to select one of these at will.
You will just have to delegate the remaining methods of ITextureRegion to the selected object.
public class MultipleTextureRegion implements ITextureRegion {
private List<TiledTextureRegion> internal;
private int selected=0;
//...
public void add(TiledTextureRegion region) {
internal.add(region);
}
public void select(int index) {
selected=index;
}
//...
// Delegates all ITextureRegion methods ...
public int getWidth() { return internal.get(selected).getWidth(); }
// And so on...
}

Drawing dynamic elements onto a normal xml layout

I've been trying to figure this out for a while now... I need to place marks over top of a seekBar to show the user places that they bookmarked in the past. The data is stored in xml. The problem is making the little ovals appear over the seekBar... It just doesn't work...
Here's my code:
public class seekMark extends View {
private int seekLength; // in pixels
private int seekLeftPad; // in pixels
private int seekBottomPad; // in pixels
private int trackLength; // in ms
private float pxOverMs; // in px/ms
ShapeDrawable lmark;
private seekMark instance;
public seekMark(Context context){
super(context);
instance = this;
seekLength = progressBar.getWidth();
seekLeftPad = progressBar.getPaddingLeft();
seekBottomPad = progressBar.getBottom();
trackLength = player.getDuration();
pxOverMs = pxPerMs();
lmark = new ShapeDrawable(new OvalShape());
}
private float pxPerMs(){
return ((float) seekLength)/((float) trackLength);
}
private int[] markPxList() throws XmlPullParserException, IOException {
int bmStartTime = 0;
String bmNames[] = bmNameList(xmlPath);
int[] bmPos = new int[bmNames.length];
for(int i=0; i < bmNames.length; i++){
bmStartTime = getBookmark(xmlPath, bmNames[i]);
bmPos[i] = (int) (bmStartTime * pxOverMs);
}
return (bmPos);
}
public void markPlace() throws XmlPullParserException, IOException {
int y = seekBottomPad;
int x = 0;
int bmPos[] = markPxList();
for(int i = 0; i < bmPos.length; i++){
x = bmPos[i] + seekLeftPad;
lmark = new ShapeDrawable();
lmark.getPaint().setColor(0xff74AC23);
lmark.setBounds(x, y, x + 1, y + 1);
instance.invalidate();
}
}
protected void onDraw(Canvas canvas) {
lmark.draw(canvas);
}
}
It's called from onCreate using this code. I call it using in another thread to avoid the problem where the dimensions of progressBar aren't yet set in onCreate.
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
if (display.getRotation() == 1){ // if landscape
final Runnable runner = new Runnable() {
public void run() {
seekMark seekMarks = new seekMark(context);
try {
seekMarks.markPlace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// runs in another thread to avoid the problem with calling
// seekMark directly from onCreate
}
};
handler.postDelayed(runner, 1000);
}
The program crashes whenever I try to call seekMark.markPlace()... I'm trying to draw this over top of my layout main.xml.
im not sure if this is what you are trying to do.
Customize Seekbar
this seems to be similar while the approach is different.

Android: custom view is too slow?

I have a custom view with a lot of png images on it(For every three characters an image). but it is too slow on drawing and scrolling.
It is my code for custom view:
public class Textview extends View
{
private String m_szText;
Context ctx;
Paint mTextPaint;
private Canvas canva;
Bitmap b ;
public Textview(Context context)
{
super(context);
ctx = context;
mTextPaint= new Paint();
mTextPaint.setTypeface(m_tTypeface);
mTextPaint.setStyle(Paint.Style.FILL);
}
public void SetText(String newtext) {
m_szText = newtext;
text(newtext);
this.invalidate();
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(text(canvas,m_szText));
}
Canvas text(Canvas canvas,String txt)
{
int left = 400;
int top = 0;
try {
for(int i=0;i<txt.length();i=i+3)
{
String adres = "glyph/" + txt.substring(i, i+3) + ".png";
Bitmap btm = getBitmapFromAsset(adres);
if(left <= 5)
{left = 400;top += btm.getHeight();}
else
left = left - btm.getWidth();
canvas.drawBitmap(btm, left ,top,mTextPaint);
}
} catch (IOException e) {
canvas.drawText(e.toString(), 50, 50, mTextPaint);
}
return canvas;
}
private Bitmap getBitmapFromAsset(String strName) throws IOException
{
AssetManager assetManager = ctx.getAssets();
InputStream istr = assetManager.open(strName);
Bitmap bitmap = BitmapFactory.decodeStream(istr);
return bitmap;
}
}
How can I speed up my custom view? I think I must to create bitmap of all images once. but how to?
thanks in advance!
You are loading and decoding several bitmaps on every draw. You need to load the bitmaps ahead of time, once and then draw them.
You can use Thread to speed up process, and there are two way to use thread
1)Implementing Runnable that override void run(){}
2)or use Thread th=new Thread(new Runnable(){void run(){}
})
The following should help. just outline of what can be done.
static HashMap<String, Bitmap> mBitmaps = new HashMap<String, Bitmap>();
public void SetText(String newtext) {
m_szText = newtext;
makeBitmap();
this.invalidate();
}
void makeBitmap()
{
for(int i=0; i<m_szText.length(); i=i+3)
{
String adres = "glyph/" + m_szText.substring(i, i+3) + ".png";
Bitmap btm = null;
if (!mBitmaps.containsKey(adres)) {
btm = getBitmapFromAsset(adres);
mBitmaps.add(adres , btm);
} else {
btm = (Bitmap)mBitmaps.get(adres);
}
length += btm.getWidth(); // considering only single line.
}
// create a new blank Bitmap of height and 'length' and assign to member.
mTextBitmap = Bitmap.createBitmap(length, height, Bitmap.Config.ARGB_8888);
// in for loop draw all the bitmaps on mTextBitmap just like you did on canvas in ur code.
}

Categories

Resources