I have the following problem, my application retrieves data from a database online as well as the texts there are also the images I am viewing via the following class. The images are displayed correctly but not to their size, I'd like that resize based on the size of the display, you know how can I do? The source is this:
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.Html.ImageGetter;
class HttpImageGetter implements ImageGetter {
#Override
public Drawable getDrawable(String source) {
try {
URL url = new URL(source);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
BitmapDrawable dr = new BitmapDrawable(BitmapFactory.decodeStream(is));
dr.setBounds(0, 0, dr.getIntrinsicWidth(), dr.getIntrinsicHeight());
return dr;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
I also have a second problem. The opening of the text is slower as there are also images that weigh more though if I click again on the name of the text shows a screen all black and sometimes it says that the application is not responding. All this happens especially when I'm with Edge connection. Do you have ideas on how I can fix and speed up the opening of the text?
PS: The images are contained in a TextView
private TextView htmlTextView;
private SpannableStringBuilder htmlSpannable;
#Override
public void onCreate(Bundle savedInstanceState) {
// ...
// first parse the html
// replace getHtmlCode() with whatever generates/fetches your html
Spanned spanned = Html.fromHtml(getHtmlCode());
// we need a SpannableStringBuilder for later use
if (spanned instanceof SpannableStringBuilder) {
// for now Html.fromHtml() returns a SpannableStringBuiler
// so we can just cast it
htmlSpannable = (SpannableStringBuilder) spanned;
} else {
// but we have a fallback just in case this will change later
// or a custom subclass of Html is used
new SpannableStringBuilder(spanned);
}
// now we can call setText() on the next view.
// this won't show any images yet
htmlTextView.setText(htmlSpannable);
// next we start a AsyncTask that loads the images
new ImageLoadTask().execute();
// ...
}
private class ImageLoadTask extends AsyncTask {
DisplayMetrics metrics = new DisplayMetrics();
#Override
protected void onPreExecute() {
// we need this to properly scale the images later
getWindowManager().getDefaultDisplay().getMetrics(metrics);
}
#Override
protected Void doInBackground(Void... params) {
// iterate over all images found in the html
for (ImageSpan img : htmlSpannable.getSpans(0,
htmlSpannable.length(), ImageSpan.class)) {
if (!getImageFile(img).isFile()) {
// here you have to download the file
}
// we use publishProgress to run some code on the
// UI thread to actually show the image
// -> onProgressUpdate()
publishProgress(img);
}
return null;
}
#Override
protected void onProgressUpdate(ImageSpan... values) {
// save ImageSpan to a local variable just for convenience
ImageSpan img = values[0];
// now we get the File object again. so remeber to always return
// the same file for the same ImageSpan object
File cache = getImageFile(img);
// if the file exists, show it
if (cache.isFile()) {
// first we need to get a Drawable object
Drawable d = new BitmapDrawable(getResources(),
cache.getAbsolutePath());
// next we do some scaling
int width, height;
int originalWidthScaled = (int) (d.getIntrinsicWidth() * metrics.density);
int originalHeightScaled = (int) (d.getIntrinsicHeight() * metrics.density);
if (originalWidthScaled > metrics.widthPixels) {
height = d.getIntrinsicHeight() * metrics.widthPixels
/ d.getIntrinsicWidth();
width = metrics.widthPixels;
} else {
height = originalHeightScaled;
width = originalWidthScaled;
}
// it's important to call setBounds otherwise the image will
// have a size of 0px * 0px and won't show at all
d.setBounds(0, 0, width, height);
// now we create a new ImageSpan
ImageSpan newImg = new ImageSpan(d, img.getSource());
// find the position of the old ImageSpan
int start = htmlSpannable.getSpanStart(img);
int end = htmlSpannable.getSpanEnd(img);
// remove the old ImageSpan
htmlSpannable.removeSpan(img);
// add the new ImageSpan
htmlSpannable.setSpan(newImg, start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// finally we have to update the TextView with our
// updates Spannable to display the image
htmlTextView.setText(htmlSpannable);
}
}
private File getImageFile(ImageSpan img) {
// you need to implement this method yourself.
// it must return a unique File object (or something
// different if you also change the rest of the code)
// for every image tag. use img.getSource() to get
// the src="" attribute. you might want to use some
// hash of the url as file name
}
}
to resize image the codes included are you can change the logic as you want 20 a scaling % or whatever you want as you have the display matrics (hight and width of screen) enjoy
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int width, height;
int originalWidthScaled = (int) (d.getIntrinsicWidth() * metrics.density);
int originalHeightScaled = (int) (d.getIntrinsicHeight() * metrics.density);
if (originalWidthScaled > metrics.widthPixels) {
height = d.getIntrinsicHeight() * metrics.widthPixels
/ d.getIntrinsicWidth();
width = metrics.widthPixels;
} else {
height = originalHeightScaled;
width = originalWidthScaled;
}
// it's important to call setBounds otherwise the image will
// have a size of 0px * 0px and won't show at all
d.setBounds(0, 0, width, height);
and loading the image in seprate thread will avoid anr
Related
I have problem with detecting if barcode is inside specified area. For testing purposes camera source preview and surface view has same size 1440x1080 to prevent scaling between camera and view. I get positive checks even if I see QR Code isn't in box what represents image. Whats wrong?
False positive check
ScannerActivity
public class ScannerActivity extends AppCompatActivity {
private static final String TAG = "ScannerActivity";
private SurfaceView mSurfaceView; // Its size is forced to 1440x1080 in XML
private CameraSource mCameraSource;
private ScannerOverlay mScannerOverlay; // Its size is forced to 1440x1080 in XML
#Override
protected void onCreate(Bundle savedInstanceState) {
// .. create and init views
// ...
BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.ALL_FORMATS)
.build();
mCameraSource = new CameraSource.Builder(this, barcodeDetector)
.setRequestedPreviewSize(1440, 1080)
.setRequestedFps(20.0f)
.setFacing(CameraSource.CAMERA_FACING_BACK)
.setAutoFocusEnabled(true)
.build();
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
#Override
public void release() {
}
#Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
parseDetections(detections.getDetectedItems());
}
});
}
private void parseDetections(SparseArray<Barcode> barcodes) {
for (int i = 0; i < barcodes.size(); i++) {
Barcode barcode = barcodes.valueAt(i);
if (isInsideBox(barcode)) {
runOnUiThread(() -> {
Toast.makeText(this, "GOT DETECTION: " + barcode.displayValue, Toast.LENGTH_SHORT).show();
});
}
}
}
private boolean isInsideBox(Barcode barcode) {
Rect barcodeBoundingBox = barcode.getBoundingBox();
Rect scanBoundingBox = mScannerOverlay.getBox();
boolean checkResult = barcodeBoundingBox.left >= scanBoundingBox.left &&
barcodeBoundingBox.right <= scanBoundingBox.right &&
barcodeBoundingBox.top >= scanBoundingBox.top &&
barcodeBoundingBox.bottom <= scanBoundingBox.bottom;
Log.d(TAG, "isInsideBox: "+(checkResult ? "YES" : "NO"));
return checkResult;
}
}
Explanation to your issue is simple, but the solution is not trivial to explain.
The coordinates of the box from your UI will mostly not the be the same like the imaginary box on each preview frame. You must transform the coordinates from the UI box to scanBoundingBox.
I open sourced an example which implement the same usecase you are trying to accomplish. In this example I took another approach, I cut the box out of each frame first before feeding it to Google Vision, which is also more efficient, since Google Vision don't have to analyse the whole picture and waste tons of CPU...
I decied to cropp frame by wrapping barcode detector however I don't know why but cropped frame is rotated by 90 degress even smarphone is upright orientation.
Box Detector class
public class BoxDetector extends Detector<Barcode> {
private Detector<Barcode> mDelegate;
private int mBoxWidth;
private int mBoxHeight;
// Debugging
private CroppedFrameListener mCroppedFrameListener;
public BoxDetector(Detector<Barcode> delegate, int boxWidth, int boxHeight) {
mDelegate = delegate;
mBoxWidth = boxWidth;
mBoxHeight = boxHeight;
}
public void setCroppedFrameListener(CroppedFrameListener croppedFrameListener) {
mCroppedFrameListener = croppedFrameListener;
}
#Override
public SparseArray<Barcode> detect(Frame frame) {
int frameWidth = frame.getMetadata().getWidth();
int frameHeight = frame.getMetadata().getHeight();
// I assume that box is centered.
int left = (frameWidth / 2) - (mBoxWidth / 2);
int top = (frameHeight / 2) - (mBoxHeight / 2);
int right = (frameWidth / 2) + (mBoxWidth / 2);
int bottom = (frameHeight / 2) + (mBoxHeight / 2);
YuvImage yuvImage = new YuvImage(frame.getGrayscaleImageData().array(), ImageFormat.NV21, frameWidth, frameHeight, null);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
yuvImage.compressToJpeg(new Rect(left, top, right, bottom), 100, outputStream);
byte[] jpegArray = outputStream.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(jpegArray, 0, jpegArray.length);
Frame croppedFrame = new Frame.Builder()
.setBitmap(bitmap)
.setRotation(frame.getMetadata().getRotation())
.build();
if(mCroppedFrameListener != null) {
mCroppedFrameListener.onNewCroppedFrame(croppedFrame.getBitmap(), croppedFrame.getMetadata().getRotation());
}
return mDelegate.detect(croppedFrame);
}
public interface CroppedFrameListener {
void onNewCroppedFrame(Bitmap bitmap, int rotation);
}
}
Box Detector usuage
BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.ALL_FORMATS)
.build();
BoxDetector boxDetector = new BoxDetector(
barcodeDetector,
mBoxSize.getWidth(),
mBoxSize.getHeight());
boxDetector.setCroppedFrameListener(new BoxDetector.CroppedFrameListener() {
#Override
public void onNewCroppedFrame(final Bitmap bitmap, int rotation) {
Log.d(TAG, "onNewCroppedFrame: new bitmap, rotation: "+rotation);
runOnUiThread(new Runnable() {
#Override
public void run() {
mPreview.setImageBitmap(bitmap);
}
});
}
});
Cropped frame is rotated
I am using GWT FileUpload() and a form to let the user select an image and upload it to the server. What I want to do is preview the image before it gets to the server. I am only able to get the file name from the FileUpload()
HorizontalPanel row = new HorizontalPanel();
row.add(accounts = new ListBox());
accounts.setWidth("200px");
row.setStyleName("accountPositioning");
accounts.setName("accounts");
final Image image = new Image();
image.setStyleName("previewImage");
setCellSpacing(10);
panel = new VerticalPanel();
panel.add(row);
panel.add(image);
final FormPanel form = new FormPanel();
form.setEncoding(FormPanel.ENCODING_MULTIPART);
form.setMethod(FormPanel.METHOD_POST);
downloadPanel = new FormPanel();
downloadPanel.setEncoding(FormPanel.ENCODING_MULTIPART);
downloadPanel.setMethod(FormPanel.METHOD_GET);
deletePanel = new FormPanel();
deletePanel.setEncoding(FormPanel.ENCODING_MULTIPART);
deletePanel.setMethod(FormPanel.METHOD_POST);
upload = new FileUpload();
upload.setName("upload");
upload.setStyleName("chooseImageButton");
upload.setEnabled(false);
upload.setVisible(false);
VerticalPanel holder = new VerticalPanel();
uploadButton = new Button("Import");
uploadButton.setEnabled(false);
uploadButton.setStyleName("importImageButton");
uploadButton.setVisible(false);
uploadButton.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
String filename = upload.getFilename();
if (filename.length() == 0) {
Window.alert("No File Specified!");
} else {
int selectedIndex = accounts.getSelectedIndex();
accountIdStr = accounts.getValue(selectedIndex);
form.setAction(GWT.getModuleBaseURL()+"uploadfile" + "?entityId="+ accountIdStr);
form.submit();
}
}
});
How can i get the file path of the image file upload using GWT FileUpload() so i can preview the image before submitting it to the server?
I am using GWT 2.7.0 version so i cant use File, or Path library
You're going to need to get the File object out of the element...
And you can do it like this:
#Override
public elemental.html.FileList fileSelected(FileUpload fileUpload) {
final elemental.html.InputElement element = (elemental.html.InputElement) fileUpload.getElement();
return element.getFiles();
}
As you can see it uses gwt elemental.
Once you've extracted the File object you can do this:
import com.google.gwt.user.client.Element;
import elemental.html.File;
import elemental.html.ImageElement;
import elemental2.dom.FileReader;
public void loadPreview(File file) {
FileReader reader = new FileReader();
reader.onloadend = pe -> {
Element element0 = this.image.getElement();
com.google.gwt.dom.client.Element element = element0;
ImageElement image = (ImageElement) element;
image.setSrc(reader.result.asString());
image.addEventListener("load", evt -> {
LOG.debug("image loaded event {}", evt);
int owidth = image.getWidth();
int oheight = image.getHeight();
LOG.debug("widht {}', height {}", owidth, oheight);
//IMAGE_VIEW_PORT_WIDTH
//IMAGE_VIEW_PORT_HEIGHT
int height;
int width;
if (owidth >= (destAspectRatio) * oheight) {
height = (int) Math.round(IMAGE_VIEW_PORT_WIDTH * (oheight / (double) owidth));
width = IMAGE_VIEW_PORT_WIDTH;
} else {
width = (int) Math.round(IMAGE_VIEW_PORT_HEIGHT * (owidth / (double) oheight));
height = IMAGE_VIEW_PORT_HEIGHT;
}
image.setWidth(width);
image.setHeight(height);
this.image.setVisible(true);
LOG.debug("new width {}, new height {}", width, height);
});
return null;
};
reader.readAsDataURL((elemental2.dom.File) file);
}
I'm sorry it's so complex - it the code that I have and I think it figures out the correct view port size so that image is set to the correct size (though I'm not 100% sure).
You might get way without the image.addEventListener("load"...) handler as I believe it's the image.setSrc(reader.result.asString()) that is the money bit.
It's using a lot of elemental and elemental2 as stock gwt just doesn't give you enough exposure to the actual API you're dealing with.
Notice also that this uses the File object from elemental.
I am trying to overlay an image inside the rectangular region after detection of face and eyes. But I am unable to do so. Can you please guide me how to proceed.
I have searched a lot on google and tried Mat copy to src file but no result. Please help me in solving this issue.
public class FXController {
// FXML buttons
#FXML
private Button cameraButton;
// the FXML area for showing the current frame
#FXML
private ImageView originalFrame;
// checkboxes for enabling/disabling a classifier
#FXML
private CheckBox haarClassifier;
#FXML
private CheckBox lbpClassifier;
// a timer for acquiring the video stream
private ScheduledExecutorService timer;
// the OpenCV object that performs the video capture
private VideoCapture capture;
// a flag to change the button behavior
private boolean cameraActive;
// face cascade classifier
private CascadeClassifier faceCascade;
private int absoluteFaceSize;
/**
* Init the controller, at start time
*/
protected void init()
{
this.capture = new VideoCapture();
this.faceCascade = new CascadeClassifier();
this.absoluteFaceSize = 0;
// set a fixed width for the frame
originalFrame.setFitWidth(600);
// preserve image ratio
originalFrame.setPreserveRatio(true);
}
/**
* The action triggered by pushing the button on the GUI
*/
#FXML
protected void startCamera()
{
if (!this.cameraActive)
{
// disable setting checkboxes
this.haarClassifier.setDisable(true);
this.lbpClassifier.setDisable(true);
// start the video capture
this.capture.open(0);
// is the video stream available?
if (this.capture.isOpened())
{
this.cameraActive = true;
// grab a frame every 33 ms (30 frames/sec)
Runnable frameGrabber = new Runnable() {
#Override
public void run()
{
// effectively grab and process a single frame
Mat frame = grabFrame();
// convert and show the frame
Image imageToShow = Utils.mat2Image(frame);
updateImageView(originalFrame, imageToShow);
}
};
this.timer = Executors.newSingleThreadScheduledExecutor();
this.timer.scheduleAtFixedRate(frameGrabber, 0, 33, TimeUnit.MILLISECONDS);
// update the button content
this.cameraButton.setText("Stop Camera");
}
else
{
// log the error
System.err.println("Failed to open the camera connection...");
}
}
else
{
// the camera is not active at this point
this.cameraActive = false;
// update again the button content
this.cameraButton.setText("Start Camera");
// enable classifiers checkboxes
this.haarClassifier.setDisable(false);
this.lbpClassifier.setDisable(false);
// stop the timer
this.stopAcquisition();
}
}
/**
* Get a frame from the opened video stream (if any)
*
* #return the {#link Image} to show
*/
private Mat grabFrame()
{
Mat frame = new Mat();
// check if the capture is open
if (this.capture.isOpened())
{
try
{
// read the current frame
this.capture.read(frame);
// if the frame is not empty, process it
if (!frame.empty())
{
// face detection
this.detectAndDisplay(frame);
}
}
catch (Exception e)
{
// log the (full) error
System.err.println("Exception during the image elaboration: " + e);
}
}
return frame;
}
/**
* Method for face detection and tracking
*
* #param frame
* it looks for faces in this frame
*/
private void detectAndDisplay(Mat frame)
{
MatOfRect faces = new MatOfRect();
//Mat grayFrameSrc = new Mat();
Mat grayFrameDest=Imgcodecs.imread("images/face.png");
// convert the frame in gray scale
//Imgproc.cvtColor(frame, grayFrameSrc, Imgproc.COLOR_BGR2GRAY);
Imgproc.cvtColor(frame, grayFrameDest, Imgproc.COLOR_BGR2GRAY);
// equalize the frame histogram to improve the result
//Imgproc.equalizeHist(grayFrameSrc, grayFrameSrc);
Imgproc.equalizeHist(grayFrameDest, grayFrameDest);
//int height = grayFrameSrc.rows();
//int width = grayFrameSrc.width();
int height = grayFrameDest.rows();
// compute minimum face size (20% of the frame height, in our case)
if (this.absoluteFaceSize == 0)
{
//System.out.println("The height = "+width);
if (Math.round(height * 0.1f) > 0)
{
this.absoluteFaceSize = Math.round(height * 0.1f);
}
}
// detect faces
this.faceCascade.detectMultiScale(grayFrameDest, faces, 1.1, 2, 0 | Objdetect.CASCADE_SCALE_IMAGE,
new Size(this.absoluteFaceSize, this.absoluteFaceSize), new Size());
// each rectangle in faces is a face: draw them!
Rect[] facesArray = faces.toArray();
for (int i = 0; i < facesArray.length; i++){
int x=facesArray[i].x;
int y=facesArray[i].y;
int h=facesArray[i].height;
int w=facesArray[i].width;
Rect rect=facesArray[i];
Imgproc.rectangle(frame, facesArray[i].tl(), facesArray[i].br(), new Scalar(0, 255, 0), 3);
//Imgproc.putText(frame, "Hi Ankit", new Point(x, y), 0, 0, new Scalar(0, 255, 0));
Imgcodecs.imwrite("/home/ankit-mathur/Desktop/mask.png", frame);
Mat temp = new Mat();
Imgproc.cvtColor(grayFrameDest, temp, Imgproc.COLOR_BGRA2GRAY,0);
Mat temp_rgba = new Mat();
Imgproc.cvtColor(temp, temp_rgba, Imgproc.COLOR_GRAY2BGRA,0);
temp_rgba.copyTo(grayFrameDest);
}
}
#FXML
protected void haarSelected(Event event)
{
// check whether the lpb checkbox is selected and deselect it
if (this.lbpClassifier.isSelected())
this.lbpClassifier.setSelected(false);
this.checkboxSelection("resources/haarcascades/haarcascade_frontalface_alt.xml");
}
/**
* The action triggered by selecting the LBP Classifier checkbox. It loads
* the trained set to be used for frontal face detection.
*/
#FXML
protected void lbpSelected(Event event)
{
// check whether the haar checkbox is selected and deselect it
if (this.haarClassifier.isSelected())
this.haarClassifier.setSelected(false);
this.checkboxSelection("resources/lbpcascades/lbpcascade_frontalface.xml");
}
/**
* Method for loading a classifier trained set from disk
*
* #param classifierPath
* the path on disk where a classifier trained set is located
*/
private void checkboxSelection(String classifierPath)
{
// load the classifier(s)
//System.out.println(classifierPath);
if (this.faceCascade.load(classifierPath)) {
this.faceCascade.load(classifierPath);
}
else{
System.out.println("Unable To Load FaceCascade");
}
// now the video capture can start
this.cameraButton.setDisable(false);
}
/**
* Stop the acquisition from the camera and release all the resources
*/
private void stopAcquisition()
{
if (this.timer!=null && !this.timer.isShutdown())
{
try
{
// stop the timer
this.timer.shutdown();
this.timer.awaitTermination(33, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e)
{
// log any exception
System.err.println("Exception in stopping the frame capture, trying to release the camera now... " + e);
}
}
if (this.capture.isOpened())
{
// release the camera
this.capture.release();
}
}
/**
* Update the {#link ImageView} in the JavaFX main thread
*
* #param view
* the {#link ImageView} to update
* #param image
* the {#link Image} to show
*/
private void updateImageView(ImageView view, Image image)
{
Utils.onFXThread(view.imageProperty(), image);
}
/**
* On application close, stop the acquisition from the camera
*/
protected void setClosed()
{
this.stopAcquisition();
}
}
I am working on the same thing on iOS. Here is how I am doing it
Mat image = imread("path/to/image");
//center.x and center.y are the location in the screen
cv::Rect roi( cv::Point( center.x, center.y), image.size() );
//if you want to save it in a Mat
image.copyTo( source( roi ));
//or if you want to write it on a file
imwrite("image.jpg", image);
I have implemented the loading screen from this example on my Loading Screen that is a child of Game. This is how my asset init method and Screen class looks like. My init method in assets loads classes that contain my AtlasRegion. I have tested that this method is what makes my screen load a black screen as it loads a lot of resources.
public void init (AssetManager assetManager) {
this.assetManager = assetManager;
// set asset manager error handler
assetManager.setErrorListener(this);
assetManager.load(Constants.TEXTURE_ATLAS_OBJECTS, TextureAtlas.class);
assetManager.load(Constants.TEXTURE_ATLAS_UI, TextureAtlas.class);
assetManager.finishLoading();
TextureAtlas atlas = assetManager.get(Constants.TEXTURE_ATLAS_OBJECTS);
TextureAtlas atlasUi = assetManager.get(Constants.TEXTURE_ATLAS_UI);
//font = new BitmapFont(Gdx.files.internal("data/font.fnt"), Gdx.files.internal("data/font.png"), false);
clickSound = Gdx.audio.newSound(Gdx.files.internal("data/sounds/click.wav"));
// create game resource objects
fonts = new AssetFonts(assetManager);
skins = new AssetSkins();
background = new AssetBackgroundImage();
cards = new AssetCards(atlas);
cardimages = new AssetImages(atlas);
cardsjson = new AssetList();
suitimages = new AssetSuitImages(atlas);
}
This is my Loading Screen class:
public class LoadingScreen extends AbstractGameScreen implements Disposable {
private Stage stage;
private Image logo;
private Image loadingFrame;
private Image loadingBarHidden;
private Image screenBg;
private Image loadingBg;
private float startX, endX;
private float percent;
private Actor loadingBar;
public LoadingScreen(CardGame game) {
super(game);
}
public InputProcessor getInputProcessor () {
return (null);
}
#Override
public void show() {
// Tell the manager to load assets for the loading screen
game.manager.load("data/images-ui/loading/loading.pack", TextureAtlas.class);
// Wait until they are finished loading
game.manager.finishLoading();
// Initialize the stage where we will place everything
stage = new Stage();
// Get our textureatlas from the manager
TextureAtlas atlas = game.manager.get("data/images-ui/loading/loading.pack", TextureAtlas.class);
// Grab the regions from the atlas and create some images
logo = new Image(atlas.findRegion("libgdx-logo"));
loadingFrame = new Image(atlas.findRegion("loading-frame"));
loadingBarHidden = new Image(atlas.findRegion("loading-bar-hidden"));
screenBg = new Image(atlas.findRegion("screen-bg"));
loadingBg = new Image(atlas.findRegion("loading-frame-bg"));
// Add the loading bar animation
Animation anim = new Animation(0.05f, atlas.findRegions("loading-bar-anim") );
anim.setPlayMode(Animation.PlayMode.LOOP_REVERSED);
loadingBar = new LoadingBar(anim);
// Or if you only need a static bar, you can do
// loadingBar = new Image(atlas.findRegion("loading-bar1"));
// Add all the actors to the stage
stage.addActor(screenBg);
stage.addActor(loadingBar);
stage.addActor(loadingBg);
stage.addActor(loadingBarHidden);
stage.addActor(loadingFrame);
stage.addActor(logo);
// Add everything to be loaded, for instance:
Assets.instance.init(game.manager);
}
#Override
public void resize(int width, int height) {
// Set our screen to always be XXX x 480 in size
//width = 480 * width / height;
//height = 480;
stage.getViewport().update(width, height, false);
// Make the background fill the screen
screenBg.setSize(width, height);
// Place the logo in the middle of the screen and 100 px up
logo.setX((width - logo.getWidth()) / 2);
logo.setY((height - logo.getHeight()) / 2 + 100);
// Place the loading frame in the middle of the screen
loadingFrame.setX((stage.getWidth() - loadingFrame.getWidth()) / 2);
loadingFrame.setY((stage.getHeight() - loadingFrame.getHeight()) / 2);
// Place the loading bar at the same spot as the frame, adjusted a few px
loadingBar.setX(loadingFrame.getX() + 15);
loadingBar.setY(loadingFrame.getY() + 5);
// Place the image that will hide the bar on top of the bar, adjusted a few px
loadingBarHidden.setX(loadingBar.getX() + 35);
loadingBarHidden.setY(loadingBar.getY() - 3);
// The start position and how far to move the hidden loading bar
startX = loadingBarHidden.getX();
endX = 440;
// The rest of the hidden bar
loadingBg.setSize(450, 50);
loadingBg.setX(loadingBarHidden.getX() + 30);
loadingBg.setY(loadingBarHidden.getY() + 3);
}
#Override
public void render(float delta) {
// Clear the screen
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (game.manager.update()) { // Load some, will return true if done loading
if (Gdx.input.isTouched()) { // If the screen is touched after the game is done loading, go to the main menu screen
game.setScreen(new MainMenuScreen(game));
}
}
// Interpolate the percentage to make it more smooth
percent = Interpolation.linear.apply(percent, game.manager.getProgress(), 0.1f);
// Update positions (and size) to match the percentage
loadingBarHidden.setX(startX + endX * percent);
loadingBg.setX(loadingBarHidden.getX() + 30);
loadingBg.setWidth(450 - 450 * percent);
loadingBg.invalidate();
// Show the loading screen
stage.act();
stage.draw();
}
#Override
public void pause () {}
#Override
public void resume () {}
#Override
public void hide() {
// Dispose the loading assets as we no longer need them
game.manager.unload("data/images-ui/loading/loading.pack");
}
}
I need to load the screen then load my assets on this Assets.instance.init(new AssetManager()) since it is what is causing a black screen. The problem is my screen loads after loading the assets hence this makes my loading screen to be of no use. How can I load this method after the screen has rendered?
Remove the finishLoading call from your init method. That should do it. finishLoading forces the assets to finish loading immediately which is not what you want here. Calling update on it repeatedly, which you are already doing is the way to load assets asynchronously.
Here's a general structure for how it could be done. Call all the load methods on the asset manager before it ever gets to the render method. Then call update repeatedly until everything is loaded. Then get references to the assets before continuing.
private boolean loadComplete;
public void show(){
//...
loadComplete = false;
}
private void init(AssetManager assetManager){
this.assetManager = assetManager;
assetManager.load(/*whatever*/);
assetManager.load(/*whatever*/);
assetManager.load(/*whatever*/);
}
private void onAssetsLoaded(){
loadComplete = true;
myAtlas = assetManager.get("myAtlas.json", TextureAtlas.class);
//and so on
}
public void render(float delta){
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//load one asset at a time per frame until loading is complete
if (assetManager == null || !assetManager.update()){
//maybe draw a load screen image that's not a texture that's being managed
//by the assetManager. You could even play an animation. Otherwise,
//you can leave the screen blank.
return;
}
//will only get here when asset manager is done. The first time, you still
//need to get references to its assets
if (!loadComplete)
onAssetsLoaded();
//the rest of you render method. Everything below here only happens after assets are loaded and asset references have been aquired
}
After trying so many recommended solutions, I finally managed to fix this problem thanks to this blog post
I used a callback which displays an android framelayout with an image as the assets load asynchronously.
I am working on an Android app that displays cell phone usage information in a progress bar. The bar changes color based on the amount of usage from green to yellow to red. When my TimerTask executes the update though (via a Handler so it goes through the UI thread and not the Timer thread), the progress bars empty, even though the text labels are updated correctly. The code updating the progress bar is:
private void SetBarColor(ProgressBar bar, int progress, int secondaryAdditive){
int setTo = R.drawable.greenbar;
if(progress < 60)
setTo = R.drawable.greenbar;
else if (progress < 90)
setTo = R.drawable.yellowbar;
else
setTo = setTo = R.drawable.redbar;
bar.setProgress(progress);
bar.setSecondaryProgress(progress + secondaryAdditive); //Mocking up phone usage
Rect bounds = bar.getProgressDrawable().getBounds();
bar.setProgressDrawable(this.getResources().getDrawable(setTo));
bar.getProgressDrawable().setBounds(bounds);
bar.setVisibility(View.VISIBLE);
}
This method works fine when called the first time in the onCreate method, but when called from the TimerTask, the bars simply hide themselves, showing only the grey background (as if their progress == 0). I've used the debugger and confirmed that the right values are going into the setProgress and setSecondaryProgress() calls. I have also tried setting the progress both before (as in the snippet above) and after the setProgressDrawable call, to no avail.
Anyone run into something like this?
EDIT: By request, some additional code. Here's the Runnable:
private class MyTime extends TimerTask {
#Override
public void run() {
ReQueryCount--;
if(ReQueryCount <= 0){
ReQueryCount = ReQueryCountStarter;
HeartbeatHandler.post(new Runnable() {
public void run() {
GetDataFromServer();
}
});
}
}
}
The HeartbeatHandler is created in onCreate.
GetDataFromServer gets some data from server, but the part that consumes my SetBarColor above is:
private void UpdateProgressBars(ServiceHelper.UsageResult result) {
int voiceBarProg = (int)((double)result.VoiceUsage / (double)result.MaxVoice * 100);
int dataBarProg = (int)((double)result.DataUsage / (double)result.MaxData * 100);
int msgBarProg = (int)((double)result.TextUsage / (double)result.MaxText * 100);
SetBarColor(voiceBar, voiceBarProg, PhoneVoice);
SetBarColor(dataBar, dataBarProg, PhoneData);
SetBarColor(msgBar, msgBarProg, PhoneText);
}
Short of posting the layouts, manifest and the rest I'm not sure what other code would be helpful.
After searching for hours for a solution of this problem I have found this article that has an answer that has helped me.
But in addition to the answer there I have also get/set the drawable bounds too. So I have a custom ProgressBar which has the following method to set progress color and amount:
public void setProgressColorAndFill(int color, int fill){
Drawable bgDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, new int[]{0xFFdadada, 0xFFdadada});
Drawable proDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, new int[]{color, color});
ClipDrawable clip = new ClipDrawable(proDrawable, Gravity.LEFT,ClipDrawable.HORIZONTAL);
Drawable[] layers = new Drawable[2];
layers[0] = bgDrawable;
layers[1] = clip;
LayerDrawable layerDrawable = new LayerDrawable(layers);
layerDrawable.setId(0, android.R.id.background);
layerDrawable.setId(1, android.R.id.progress);
Rect bounds = ((LayerDrawable)getProgressDrawable()).getBounds();
setProgressDrawable(layerDrawable);
getProgressDrawable().setBounds(bounds);
setProgress(1);
setMax(100);
setProgress(fill);
}
Hope this will be useful to someone.