Android Volley - how to animate image loading? - android

any idea how to play a fade in animation when image loads? Now it just blinks into place. I am using NetworkImageView from the Volley toolkit.
Also, is there a way to set loading and error bitmaps on the network image view without using the ImageLoader.get( .. ) ?
Thanks!
//EDIT: Okay, thanks to you all, but if we want to be perfectionists, we should only animate if loading from disk cache, overriding setImageBitmap would case animation to go off even if pulled from memcache
what you want to do is add a boolean shouldAnimate to ImageListener.onResponse like this
public static ImageListener getImageListener(final ImageView view, final int defaultImageResId,
final int errorImageResId) {
return new ImageListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (errorImageResId != 0) {
view.setImageResource(errorImageResId);
}
}
#Override
public void onResponse(ImageContainer response, boolean isImmediate, boolean shouldAnimate) {
if (response.getBitmap() != null) {
if (shouldAnimate) {
// ADDED
view.setAlpha(0f);
view.setImageBitmap(response.getBitmap());
view.animate().alpha(1f).setDuration(1000);
// END ADDED
} else {
view.setImageBitmap(response.getBitmap());
}
} else if (defaultImageResId != 0) {
view.setImageResource(defaultImageResId);
}
}
};
}
this is a method that sets the bitmap, no matter where it is from, so you need to set it to false to every usage in ImageLoader except for
class BatchedImageRequest {
private void batchResponse(String cacheKey, BatchedImageRequest request,
final VolleyError error) {
...
container.mListener.onResponse(container, false, true);
...
}
}
I've created a Gist for Copy & Paste usage - https://gist.github.com/ursusursus/5732521

Example implementation of CommonsWare's answer can be found here: https://gist.github.com/benvd/5683818.
Using a TransitionDrawable does add an extra layer of overdraw. If you want to avoid that, perhaps using a ViewPropertyAnimator might help.
The gist of it is basically to have the following snippet in your setImageBitmap():
TransitionDrawable td = new TransitionDrawable(new Drawable[]{
new ColorDrawable(android.R.color.transparent),
new BitmapDrawable(getContext().getResources(), bm)
});
setImageDrawable(td);
td.startTransition(FADE_IN_TIME_MS);

any idea how to play a fade in animation when image loads? Now it just blinks into place. I am using NetworkImageView from the Volley toolkit.
Off the cuff, create your own subclass of NetworkImageView and override setImageBitmap() to set up the alpha animation when the image is applied.
Also, is there a way to set loading and error bitmaps on the network image view without using the ImageLoader.get( .. ) ?
Call setImageBitmap() or setImageResource(), the same way you would with a regular ImageView.

This is my implementation of an animating NetworkImageView. It uses ObjectAnimator to fade in newly downloaded images, while cached images appear immediately. The implementation is pretty simple - only requiring you to override two NetworkImageView methods (setImageBitmap(Bitmap) and setImageUrl(String, ImageLoader). It uses the supplied ImageLoader to determine if the image is from cache.
public class AnimatedNetworkImageView extends NetworkImageView {
private static final int ANIM_DURATION = 500;
private boolean shouldAnimate = false;
public AnimatedNetworkImageView(Context context) {
super(context);
}
public AnimatedNetworkImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AnimatedNetworkImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
if(shouldAnimate) {
ObjectAnimator.ofFloat(this, "alpha", 0, 1).setDuration(ANIM_DURATION).start();
}
}
#Override
public void setImageUrl(String url, ImageLoader imageLoader) {
shouldAnimate = !imageLoader.isCached(url, 0, 0);
super.setImageUrl(url, imageLoader);
}
}
Hope this helps someone!

Also, is there a way to set loading and error bitmaps on the network
image view without using the ImageLoader.get( .. ) ?
NetworkImageView has methods for these two operations :
mNetworkImageView.setErrorImageResId(R.drawable.errorImageResourceId);
mNetworkImageView.setDefaultImageResId(R.drawable.loadingImageResourceId);

i recommend to use Picasso lib for loading images or Glide.
for loading images + animation use Picasso with this lib: com.github.florent37:materialimageloading
solution like this..
Picasso.with(this).load(bannerUrl).fit().centerCrop().into(bannerImage, new Callback() {
#Override
public void onSuccess() {
MaterialImageLoading.animate(bannerImage).setDuration(2000).start();
}
#Override
public void onError() {
}
});
if you maybe want some auth stuff for image loading use Glide like this..
GlideUrl glideUrl = new GlideUrl(bannerUrl, new LazyHeaders.Builder()
.addHeader("username", "heyyou")
.addHeader("password", "heyyou")
.build());
Glide.with(this).load(bannerUrl)
.centerCrop()
.crossFade(5000)
.into(bannerImage);
in addition Picasso for auth stuff have interceptor things but i used Glide.
maybe this helps..

This version of the NetworkImageView checks the ImageLoader upon url set if the image is in the cache and disables the animation. In contrast to #DalvikDroid answer this also respects the with/height/scalingType parameter for the cache.
/**
* NetworkImageView that fades-in the drawable upon complete download
*/
public class AnimatedNetworkImageView extends NetworkImageView {
private static final int ANIM_DURATION = 500;
private boolean shouldAnimate = true;
public AnimatedNetworkImageView(Context context) {
super(context);
init();
}
public AnimatedNetworkImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AnimatedNetworkImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
shouldAnimate = true; // animate by default. Only when {#link #determineIfAnimationIsNecessary} is called animation is dependent upon cache status
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
if (shouldAnimate) {
// your animation. Here with ObjectAnimator for example
ObjectAnimator.ofFloat(this, "alpha", 0, 1).setDuration(ANIM_DURATION).start();
}
}
#Override
public void setImageUrl(String url, ImageLoader imageLoader) {
shouldAnimate = determineIfAnimationIsNecessary(url, imageLoader);
super.setImageUrl(url, imageLoader);
}
/**
* checks if for the given imgUrl and imageLoader the view should animate when a bitmap is set.
* If this method is called before {#link NetworkImageView#setImageUrl(String, ImageLoader)} is called the view would not be animated if the image comes from the cache.
*
* #param imgUrl the image url
* #param imageLoader the image loader
*/
public boolean determineIfAnimationIsNecessary(String imgUrl, ImageLoader imageLoader) {
int width = getWidth();
int height = getHeight();
ScaleType scaleType = getScaleType();
boolean wrapWidth = false, wrapHeight = false;
if (getLayoutParams() != null) {
wrapWidth = getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT;
wrapHeight = getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT;
}
// Calculate the max image width / height to use while ignoring WRAP_CONTENT dimens.
int maxWidth = wrapWidth ? 0 : width;
int maxHeight = wrapHeight ? 0 : height;
return !imageLoader.isCached(imgUrl, maxWidth, maxHeight, scaleType);
}
}

Related

Is there any way to mark ProgressBar OR SeekBar at some position with different color just like "Youtube" Ad marker?

Actually I implemented custom video player in my app, also I'm showing video progress using SeekBar View. Now I want to show some marker just like "Youtube" Ad marker on my video progress bar, the number of markers and their respective position/index will be decide at runtime. I found this question (How to mark horizontal ProgressBar with different color at some index just like Youtube video yellow color ad marker in Android) asked before on stackoverflow, but unfortunately there is no solution yet. Please check below image to understand what exactly I want to achieve.
https://drive.google.com/open?id=1qxfsTu8WOPMIlek7616rVQgzab8CFVcp
Any help will be really very appreciated. Thank you.
Hi #PKMBSD I know its quite late, but check my answer below which will surely help you to achieve what you want--
Step-1] Create one "attrs.xml" file in "res/values/" folder and paste below code in that file--
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DottedSeekBar">
<attr name="dots_positions" format="reference"/>
<attr name="dots_drawable" format="reference"/>
</declare-styleable>
</resources>
Step-2] Prepare one image icon which you want to use to mark on progress bar and name it "video_mark.png".
Step-3] Create one custom SeekBar as below--
public class DottedSeekBar extends AppCompatSeekBar {
/** Int values which corresponds to dots */
private int[] mDotsPositions = null;
/** Drawable for dot */
private Bitmap mDotBitmap = null;
public DottedSeekBar(final Context context) {
super(context);
init(null);
}
public DottedSeekBar(final Context context, final AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public DottedSeekBar(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
/**
* Initializes Seek bar extended attributes from xml
*
* #param attributeSet {#link AttributeSet}
*/
private void init(final AttributeSet attributeSet) {
final TypedArray attrsArray = getContext().obtainStyledAttributes(attributeSet, R.styleable.DottedSeekBar, 0, 0);
final int dotsArrayResource = attrsArray.getResourceId(R.styleable.DottedSeekBar_dots_positions, 0);
if (0 != dotsArrayResource) {
mDotsPositions = getResources().getIntArray(dotsArrayResource);
}
final int dotDrawableId = attrsArray.getResourceId(R.styleable.DottedSeekBar_dots_drawable, 0);
if (0 != dotDrawableId) {
mDotBitmap = BitmapFactory.decodeResource(getResources(), dotDrawableId);
}
}
/**
* #param dots to be displayed on this SeekBar
*/
public void setDots(final int[] dots) {
mDotsPositions = dots;
invalidate();
}
/**
* #param dotsResource resource id to be used for dots drawing
*/
public void setDotsDrawable(final int dotsResource)
{
mDotBitmap = BitmapFactory.decodeResource(getResources(), dotsResource);
invalidate();
}
#Override
protected synchronized void onDraw(final Canvas canvas) {
super.onDraw(canvas);
final float width=getMeasuredWidth()-getPaddingLeft()-getPaddingRight();
final float step=width/(float)(getMax());
if (null != mDotsPositions && 0 != mDotsPositions.length && null != mDotBitmap) {
// draw dots if we have ones
for (int position : mDotsPositions) {
canvas.drawBitmap(mDotBitmap, position * step, 0, null);
}
}
}
}
Step-4] Use this custom SeekBar in your activity.xml file as below--
<com.your_package.DottedSeekBar
android:id="#+id/videoProgress"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Step-5] Add below code in "onCreate()" method of your "Activity.java" class--
DottedSeekBar videoProgress = (DottedSeekBar) findViewById(R.id.videoProgress);
// Disable SeekBar Thumb Drag. (Optional)
videoProgress.setOnTouchListener(new View.OnTouchListener()
{
#Override
public boolean onTouch(View view, MotionEvent motionEvent)
{
return true;
}
});
// Set custom thumb icon here (Optional)
videoProgress.getThumb().setColorFilter(getResources().getColor(R.color.cerulean_blue), PorterDuff.Mode.SRC_IN);
// Add below line to avoid unnecessary SeekBar padding. (Optional)
videoProgress.setPadding(0, 0, 0, 0);
// Handler to update video progress time--
handler = new Handler();
// Define the code block to be executed
final Runnable runnableCode = new Runnable() {
#Override
public void run()
{
updateCurrentTime();
// Repeat this the same runnable code block again another 1 seconds
// 'this' is referencing the Runnable object
handler.postDelayed(this, 1000);
}
};
yourVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener()
{
#Override
public void onPrepared(MediaPlayer mp)
{
String strTotalDuration = msToTimeConverter(vidView.getDuration());
String[] strTimeArr = strTotalDuration.split(":");
int min = Integer.parseInt(strTimeArr[0]);
int videoLengthInSec = Integer.parseInt(strTimeArr[1]);
videoLengthInSec = videoLengthInSec + (min*60);
videoProgress.setProgress(0);
videoProgress.setMax(videoLengthInSec);
// Start the initial runnable task by posting through the handler
handler.post(runnableCode);
initVideoMarkers();
}
}
);
Step-6] Copy below required methods in your "Activity.java" class--
// Method to update time progress
private void updateCurrentTime()
{
if (videoProgress.getProgress() >= 100)
{
handler.removeMessages(0);
}
String currentPosition = msToTimeConverter(vidView.getCurrentPosition());
String[] strArr = currentPosition.split(":");
int progress = vidView.getCurrentPosition() * videoLengthInSec / vidView.getDuration();
videoProgress.setProgress(progress);
}
// Milliseconds to Time converter Method
String msToTimeConverter(int millis)
{
return String.format("%02d:%02d", TimeUnit.MILLISECONDS.toMinutes(millis) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
}
// Method to set Marker values
private void initVideoMarkers()
{
videoProgress.setDots(new int[] {10, 15, 20});
videoProgress.setDotsDrawable(R.drawable.video_mark);
}
I had the same challenge and I'm using this solution, it works 100% fine. Hope it will help you.

Creating animated splash using GIF and Glide library

I want to create animated splash screen using gif image. I have used Glide library because it supports gif images.
I have done following things to achieve this:
Created splash.xml with an imageview in it.
<ImageView
android:id="#+id/iv_gif_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Inside SplashActivity.java
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
ImageView ivImage = (ImageView) findViewById(R.id.iv_gif_view);
Glide.with(this).load(R.drawable.splash_xxhdpi_2)
.asGif().into(ivImage);
}
But when I run the application screen goes black nothing appears on the screen. I'm using Glide compile 'com.github.bumptech.glide:glide:3.6.1'
You can try this way
public class GifImageView extends View {
private InputStream mInputStream;
private Movie mMovie;
private int mWidth, mHeight;
private long mStart;
private Context mContext;
public GifImageView(Context context) {
super(context);
this.mContext = context;
}
public GifImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
if (attrs.getAttributeName(1).equals("background")) {
int id = Integer.parseInt(attrs.getAttributeValue(1).substring(1));
setGifImageResource(id);
}
}
private void init() {
setFocusable(true);
mMovie = Movie.decodeStream(mInputStream);
mWidth = mMovie.width();
mHeight = mMovie.height();
requestLayout();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mWidth, mHeight);
}
#Override
protected void onDraw(Canvas canvas) {
long now = SystemClock.uptimeMillis();
if (mStart == 0) {
mStart = now;
}
if (mMovie != null) {
int duration = mMovie.duration();
if (duration == 0) {
duration = 1000;
}
int relTime = (int) ((now - mStart) % duration);
mMovie.setTime(relTime);
mMovie.draw(canvas, 0, 0);
invalidate();
}
}
public void setGifImageResource(int id) {
mInputStream = mContext.getResources().openRawResource(id);
init();
}
public void setGifImageUri(Uri uri) {
try {
mInputStream = mContext.getContentResolver().openInputStream(uri);
init();
} catch (FileNotFoundException e) {
Log.e("GIfImageView", "File not found");
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GifImageView gifImageView = (GifImageView) findViewById(R.id.GifImageView);
gifImageView.setGifImageResource(R.drawable.android);
}
}
http://www.mavengang.com/2016/05/02/gif-animation-android/
http://www.geeks.gallery/how-to-display-the-animated-gif-image-in-android/
Android does not support GIF so avoid using it since it is memory consuming. Glide is nice image caching library and luckily it supports gif images.
I'm sharing alternate and efficient way to achieve animated splash screen.
Create splash.xml as usual with an image view.
Take out each frame from your gif image. I have used splash_hdpi_1, splash_hdpi_2, splash_hdpi_3 for example.
create splash_gif_animation.xml in drawable with below code
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false" >
<item
android:drawable="#drawable/splash_hdpi_1"
android:duration="150"/>
<item
android:drawable="#drawable/splash_hdpi_2"
android:duration="150"/>
<item
android:drawable="#drawable/splash_hdpi_3"
android:duration="150"/>
</animation-list>
In your SplashActivity.java put below code
ImageView ivImage = (ImageView) findViewById(R.id.your_image_view);
ivImage.setBackgroundResource(R.drawable.splash_gif_animation);
AnimationDrawable splashAnimation = (AnimationDrawable) ivImage.getBackground();
splashAnimation.start();
You are done:)
Try this:
ImageView ivImage= (ImageView) findViewById(R.id.iv_gif_view);
GlideDrawableImageViewTarget imageViewTarget = new GlideDrawableImageViewTarget(ivImage);
Glide.with(this)
.load(R.drawable.splash_xxhdpi_2)
.into(imageViewTarget);

Play Gif Once using Fresco Library

I am using the following code to play the gif, but here it keeps on repeating the gif play.
Uri uri;
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(uri)
.setAutoPlayAnimations(true)
. // other setters
.build();
mSimpleDraweeView.setController(controller);
How can i play it once?
Well, I have not used fresco for GIF file but might be this one could help you out. Use below library for GIF file loading.
Refer this link
This is my code, and it works well:
public class MainActivity extends AppCompatActivity {
private GifImageView mGigImageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGigImageView = (GifImageView) findViewById(R.id.mgif);
GifDrawable gifDrawable = null;
try {
gifDrawable = new GifDrawable(getResources(), R.drawable.ani_1);
gifDrawable.setLoopCount(1);
} catch (IOException e) {
e.printStackTrace();
}
mGigImageView.setImageDrawable(gifDrawable);
}
}
You can use the Glide for GIF..It is better library.
Glide is quite similar to Picasso but this is much faster than Picasso.
Glide consumes less memory than Picasso.
What that Glide has but Picasso doesn't
An ability to load GIF Animation to a simple ImageView might be the most interesting feature of Glide. And yes, you can't do that with Picasso.
Some important links-
1. https://github.com/bumptech/glide
2. http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en
You can also use the Ion library to load the gif. Refer this link click here
Gradle dependencies for it:-
dependencies {
...
compile 'com.android.support:support-v4:23.1.0'
compile 'com.koushikdutta.ion:ion:2.+'
}
To load the gif from the drawable like below:-
ImageView imgView=(ImageView) view.findViewById(R.id.imageView);
Ion.with(imgView)
.error(R.drawable.error_image)
.animateGif(AnimateGifMode.ANIMATE)
.load("android.resource://" + PackageName + "/" + R.drawable.loadingbh)
.withBitmapInfo();
And load the Image from the URL like :-
Ion.with(imgView)
.error(R.drawable.error_image)
.animateGif(AnimateGifMode.ANIMATE)
.load("https://www.beginnersheap.com/wp-content/uploads/2016/08/loading-BH.gif") ///LOAD YOUR URL GIF
.withBitmapInfo();
package com.splash;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import com.exp.R;
public class SplashViewAnimation extends View {
private Movie mMovie;
private long mMovieStart;
public SplashViewAnimation(Context context) {
super(context);
setFocusable(true);
java.io.InputStream is;
is = context.getResources().openRawResource(R.drawable.coca_cola2); // Put your gif file here..
mMovie = Movie.decodeStream(is);
}
public SplashViewAnimation(Context context, AttributeSet attrSet) {
super(context, attrSet);
setFocusable(true);
java.io.InputStream is;
is = context.getResources().openRawResource(R.drawable.coca_cola2);
mMovie = Movie.decodeStream(is);
}
public SplashViewAnimation(Context context, AttributeSet attrSet, int defStyle) {
super(context, attrSet, defStyle);
setFocusable(true);
java.io.InputStream is;
is = context.getResources().openRawResource(R.drawable.coca_cola2);
mMovie = Movie.decodeStream(is);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0x00000000);
Paint p = new Paint();
p.setAntiAlias(true);
long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) { // first time
mMovieStart = now;
}
if(mMovie != null){
int dur = mMovie.duration();
if (dur == 0)
{
dur = 1000;
}
int relTime = (int) ((now - mMovieStart) % dur);
mMovie.setTime(relTime);
mMovie.draw(canvas,(int)((getWidth() - mMovie.width()) - getWidth()*.80),
(int)((getHeight() - (mMovie.height()-6)) - getHeight()*.10));
invalidate();
}
}
}
/// Use above code in main class :-
SplashViewAnimation sp = new SplashViewAnimation(this);
FrameLayout mainLayout = (FrameLayout)findViewById(R.id.main_layout_id);
LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
layoutParams.gravity= Gravity.BOTTOM | Gravity.LEFT;
mainLayout.addView(sp,layoutParams);
I know this is a very late reply. However, I couldn't find a correct answer after searching for over a couple of hours(I wanted to use only fresco and avoid adding another library for just one GIF). This could help someone like me searching for the answer. I could finally achieve it with the following workaround.
ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {
#Override
public void onFinalImageSet(
String id,
#Nullable ImageInfo imageInfo,
#Nullable final Animatable anim) {
if (anim != null) {
try {
//This is important to handle the loop. We are getting the field from class and //setting the loop count
Field field = AbstractAnimatedDrawable.class.getDeclaredField("mLoopCount");
field.setAccessible(true);
field.set(anim, 1);
} catch (Exception e) {
e.printStackTrace();
}
anim.start();
}
}
};
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(uri).setAutoPlayAnimations(false).setControllerListener(controllerListener)
// other setters
.build();
simpleImageView.setController(controller);
Hope this helps.
Thank you.

Bitmap too large to be uploaded into a texture (5312x2988, max=4096x4096) - How do I resolve this issue?

This is the tutorial I started with.
http://www.androidhive.info/2014/12/android-uploading-camera-image-video-to-server-with-progress-bar/
Basically, I've uploaded an image and and I want to display it in a custom image viewer.
I've adjusted my php.ini like so to accept large uploads.
memory_limit = 128M
post_max_size = 1G
file_uploads = On
upload_max_filesize = 1G
max_file_uploads = 20
This is the error I'm getting from the logcat
03-15 01:06:36.843: W/OpenGLRenderer(14403): Bitmap too large to be uploaded into a texture (5312x2988, max=4096x4096)
OK, so the problem is, when I try to display a picture that's 6M, 8M, 9M or 12MEGAPIXELS everything is fine. When I try display a 16Megapixels photo, it doesn't display.
Ok, but the image viewer in the tutorial displays the image. But I'm using a different class to display the images. Image viewer seems to display an image from you SdCard. FeedImageView allows you to display a picture saved on a server.
public class FeedImageView extends ImageView {
public interface ResponseObserver {
public void onError();
public void onSuccess();
}
private ResponseObserver mObserver;
public void setResponseObserver(ResponseObserver observer) {
mObserver = observer;
}
/**
* The URL of the network image to load
*/
private String mUrl;
/**
* Resource ID of the image to be used as a placeholder until the network
* image is loaded.
*/
private int mDefaultImageId;
/**
* Resource ID of the image to be used if the network response fails.
*/
private int mErrorImageId;
/**
* Local copy of the ImageLoader.
*/
private ImageLoader mImageLoader;
/**
* Current ImageContainer. (either in-flight or finished)
*/
private ImageContainer mImageContainer;
public FeedImageView(Context context) {
this(context, null);
}
public FeedImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FeedImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* Sets URL of the image that should be loaded into this view. Note that
* calling this will immediately either set the cached image (if available)
* or the default image specified by
* {#link VolleyImageView#setDefaultImageResId(int)} on the view.
*
* NOTE: If applicable, {#link VolleyImageView#setDefaultImageResId(int)}
* and {#link VolleyImageView#setErrorImageResId(int)} should be called
* prior to calling this function.
*
* #param url
* The URL that should be loaded into this ImageView.
* #param imageLoader
* ImageLoader that will be used to make the request.
*/
public void setImageUrl(String url, ImageLoader imageLoader) {
mUrl = url;
mImageLoader = imageLoader;
// The URL has potentially changed. See if we need to load it.
loadImageIfNecessary(false);
}
/**
* Sets the default image resource ID to be used for this view until the
* attempt to load it completes.
*/
public void setDefaultImageResId(int defaultImage) {
mDefaultImageId = defaultImage;
}
/**
* Sets the error image resource ID to be used for this view in the event
* that the image requested fails to load.
*/
public void setErrorImageResId(int errorImage) {
mErrorImageId = errorImage;
}
/**
* Loads the image for the view if it isn't already loaded.
*
* #param isInLayoutPass
* True if this was invoked from a layout pass, false otherwise.
*/
private void loadImageIfNecessary(final boolean isInLayoutPass) {
final int width = getWidth();
int height = getHeight();
boolean isFullyWrapContent = getLayoutParams() != null
&& getLayoutParams().height == LayoutParams.WRAP_CONTENT
&& getLayoutParams().width == LayoutParams.WRAP_CONTENT;
// if the view's bounds aren't known yet, and this is not a
// wrap-content/wrap-content
// view, hold off on loading the image.
if (width == 0 && height == 0 && !isFullyWrapContent) {
return;
}
// if the URL to be loaded in this view is empty, cancel any old
// requests and clear the
// currently loaded image.
if (TextUtils.isEmpty(mUrl)) {
if (mImageContainer != null) {
mImageContainer.cancelRequest();
mImageContainer = null;
}
setDefaultImageOrNull();
return;
}
// if there was an old request in this view, check if it needs to be
// canceled.
if (mImageContainer != null && mImageContainer.getRequestUrl() != null) {
if (mImageContainer.getRequestUrl().equals(mUrl)) {
// if the request is from the same URL, return.
return;
} else {
// if there is a pre-existing request, cancel it if it's
// fetching a different URL.
mImageContainer.cancelRequest();
setDefaultImageOrNull();
}
}
// The pre-existing content of this view didn't match the current URL.
// Load the new image
// from the network.
ImageContainer newContainer = mImageLoader.get(mUrl,
new ImageListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (mErrorImageId != 0) {
setImageResource(mErrorImageId);
}
if (mObserver != null) {
mObserver.onError();
}
}
#Override
public void onResponse(final ImageContainer response,
boolean isImmediate) {
// If this was an immediate response that was delivered
// inside of a layout
// pass do not set the image immediately as it will
// trigger a requestLayout
// inside of a layout. Instead, defer setting the image
// by posting back to
// the main thread.
if (isImmediate && isInLayoutPass) {
post(new Runnable() {
#Override
public void run() {
onResponse(response, false);
}
});
return;
}
int bWidth = 0, bHeight = 0;
if (response.getBitmap() != null) {
setImageBitmap(response.getBitmap());
bWidth = response.getBitmap().getWidth();
bHeight = response.getBitmap().getHeight();
adjustImageAspect(bWidth, bHeight);
} else if (mDefaultImageId != 0) {
setImageResource(mDefaultImageId);
}
if (mObserver != null) {
mObserver.onSuccess();
}
}
});
// update the ImageContainer to be the new bitmap container.
mImageContainer = newContainer;
}
private void setDefaultImageOrNull() {
if (mDefaultImageId != 0) {
setImageResource(mDefaultImageId);
} else {
setImageBitmap(null);
}
}
#Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
loadImageIfNecessary(true);
}
#Override
protected void onDetachedFromWindow() {
if (mImageContainer != null) {
// If the view was bound to an image request, cancel it and clear
// out the image from the view.
mImageContainer.cancelRequest();
setImageBitmap(null);
// also clear out the container so we can reload the image if
// necessary.
mImageContainer = null;
}
super.onDetachedFromWindow();
}
#Override
protected void drawableStateChanged() {
super.drawableStateChanged();
invalidate();
}
/*
* Adjusting imageview height
*/
private void adjustImageAspect(int bWidth, int bHeight) {
LinearLayout.LayoutParams params = (LayoutParams) getLayoutParams();
if (bWidth == 0 || bHeight == 0)
return;
int swidth = getWidth();
int new_height = 0;
new_height = swidth * bHeight / bWidth;
params.width = swidth;
params.height = new_height;
setLayoutParams(params);
}
}
How can I allow the picture to show with 16 megapixels in the FeedImageView class like the regular
private ImageView imgPreview;
class the tutorial uses?
Set
android:hardwareAccelerated="false"
&
android:largeHeap="true"
within the application tag of the manifest file.
It seems like this will work fine for now. The animations are a tad bit choppy as I scroll the the pictures with these parameters set.
<application
android:name="com.clxxxii.givenchy"
android:allowBackup="true"
android:hardwareAccelerated="false"
android:icon="#drawable/givenchy"
android:label="#string/app_name"
android:largeHeap="true"
android:theme="#style/CustomActionBarTheme" >
For displaying the image on image view, I prefer resizing the image and loading that image.
You can resize the image by referring the below mentioned link and this will also going to solve texture issue
http://developer.android.com/training/displaying-bitmaps/index.html

How to get when an ImageView is completely loaded in Android

I'm developing an App which draws lines over a bunch of Images. To choose these images, I have a radio group and, whenever the user clicks in a radio button, the image is load with all its own drawings.
In my radio listenner I have the following code:
bitmap = BitmapUtils.decodeSampledBitmapFromResource(root + DefinesAndroid.CAMINHO_SHOPPINGS_SDCARD + nomeImagemAtual, size.x, size.y);
mImage.setImageBitmap(bitmap);
mImage.setDrawLines(true);
mImage.setImageBitmap(loadBitmapFromView(mImage));
the decodeSampledBitmapFromResource method I got from this link on android developers (it loads bitmaps more effitiently) http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
And here's the method I call to get a Bitmap of a View
public static Bitmap loadBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap( v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.layout(0, 0, v.getLayoutParams().width, v.getLayoutParams().height);
v.draw(c);
return b;
}
I'm setting the image Bitmap of mImage because I'm using ImageViewTouch library (which enables pinch zooming over an ImageView) and if I don't do it, all the canvas drawing is deleted with any interaction over the image (like zooming in/out).
The error log is the following
07-11 21:13:41.567: E/AndroidRuntime(20056): java.lang.IllegalArgumentException: width and height must be > 0
07-11 21:13:41.567: E/AndroidRuntime(20056): at android.graphics.Bitmap.createBitmap(Bitmap.java:638)
07-11 21:13:41.567: E/AndroidRuntime(20056): at android.graphics.Bitmap.createBitmap(Bitmap.java:620)
I'm almost sure that this error is occuring cause the image bitmap is not completely loaded when I call getBitmapFromView method.
How can I know when the view is loaded completely?
Call loadBitmapFromView in a such way:
mImage.post(new Runnable() {
#Override
public void run() {
loadBitmapFromView(mImage);
}
});
Runnable provided to post() method will be executed after view measuring and layouting, so getWidth() and getHeight() will return actual width and height.
What else can you do, is measuring View manually, by invoking measure, and then taking result from getMeasuredWidth() and getMeasuredHeight(). But I do not recommend this way.
There is actually another, more reliable way to do that by using ViewTreeObserver.OnPreDrawListener. And here is an example:
mImage.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
try {
loadBitmapFromView(mImage);
// Note that returning "true" is important,
// since you don't want the drawing pass to be canceled
return true;
} finally {
// Remove listener as further notifications are not needed
mImage.getViewTreeObserver().removeOnPreDrawListener(this);
}
}
});
Using OnPreDrawListener guarantees that View was measured and layouted, while View#post(Runnable) just executes your Runnable when all Views are already most likely measured and layouted.
Below is a working NotifyImageView class that incorporates Dmitry's method above
and adds code to notify only after the ImageView is truly usable.
I hope you find it useful.
`
public interface NotifyImageHolder {
public void notifyImageChanged(final NotifyImageView thePosterImage, final int width, final int height);
}
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
public class NotifyImageView extends ImageView {
private boolean mImageChanged;
private NotifyImageHolder mHolder;
private boolean mImageFinished;
public NotifyImageView(Context context) {
super(context);
init();
}
public NotifyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public NotifyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
protected void init() {
mImageChanged = false;
mImageFinished = false;
mHolder = null;
monitorPreDraw();
}
// so we can tell when the image finishes loading..
protected void monitorPreDraw() {
final NotifyImageView thePosterImage = this;
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
try {
return true; //note, that "true" is important, since you don't want drawing pass to be canceled
} finally {
getViewTreeObserver().removeOnPreDrawListener(this); // we don't need any further notifications
thePosterImage.buildDrawingCache();
mImageFinished = true;
}
}
});
}
public void setNotifyImageHolder(NotifyImageHolder holder) {
this.mHolder = holder;
}
public boolean isImageChanged() {
return mImageChanged;
}
public boolean isImageFinished() {
return mImageFinished;
}
public void notifyOff() {
mHolder = null;
}
// the change notify happens here..
#Override
public void setImageDrawable(Drawable noPosterImage) {
super.setImageDrawable(noPosterImage);
if (mHolder != null && mImageFinished) {
mImageFinished = false; // we send a single change-notification only
final NotifyImageView theNotifyImageView = this;
theNotifyImageView.post(new Runnable() {
#Override
public void run() {
if (mHolder != null) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
mImageChanged = true;
mHolder.notifyImageChanged(theNotifyImageView, width, height);
}
}
});
}
}
}
`

Categories

Resources