-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Problem with play gif in several Views #480
Comments
You can use this example for test: import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
public class MainActivity extends AppCompatActivity {
private LinearLayout rootLayout;
private GifDrawable gifDrawable;
private ImageView imageViewA;
private ImageView imageViewB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rootLayout = new LinearLayout(this);
rootLayout.setOrientation(LinearLayout.VERTICAL);
rootLayout.setGravity(CENTER_HORIZONTAL);
Button btChangeGif = new Button(this);
btChangeGif.setText("load gifs");
btChangeGif.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loadGifs();
}
});
addView(btChangeGif, WRAP_CONTENT);
Button btResetGifGif = new Button(this);
btResetGifGif.setText("change second gif");
btResetGifGif.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
changeSecondGif();
}
});
addView(btResetGifGif, WRAP_CONTENT);
imageViewA = new ImageView(this);
imageViewA.setAdjustViewBounds(true);
addView(imageViewA, MATCH_PARENT);
imageViewB = new ImageView(this);
imageViewB.setAdjustViewBounds(true);
addView(imageViewB, MATCH_PARENT);
addContentView(rootLayout, new LinearLayout.LayoutParams(
MATCH_PARENT,
MATCH_PARENT));
}
private void addView(View view, int width) {
rootLayout.addView(view, new LinearLayout.LayoutParams(
width,
WRAP_CONTENT));
}
private MultiCallback multiCallback;
private void loadGifs() {
GifDrawable gifDrawable = getGifSample();
multiCallback = new MultiCallback();
imageViewA.setImageDrawable(gifDrawable);
multiCallback.addView(imageViewA);
imageViewB.setImageDrawable(gifDrawable);
multiCallback.addView(imageViewB);
gifDrawable.setCallback(multiCallback);
}
private void changeSecondGif() {
multiCallback.removeView(imageViewB); //remove second view
/**
* load different gif. But the same problem will be if we call imageViewB.setImageDrawable(null)
*/
GifDrawable newGifDrawable = loadGif("gif_sample2.gif");
/**
* Set new gif. This call will stuck first gif animation.
* But the same problem will be if we call 'imageViewB.setImageDrawable(null)' or 'imageViewB.setImageBitmap(...)' etc
*
*/
imageViewB.setImageDrawable(newGifDrawable);
/**
* imageViewB.setImageDrawable call broke animation in imageViewA
* because android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable) call 'setCallback(null);' to my gifDrawable
*
*
* gifDrawable.start(), gifDrawable.reset() etc. not help
*/
}
private GifDrawable getGifSample() {
if (gifDrawable == null) {
gifDrawable = loadGif("gif_sample.gif");
}
return gifDrawable;
}
private GifDrawable loadGif(String assetName) {
GifDrawableBuilder gifBuilder = new GifDrawableBuilder();
try {
return gifBuilder.from(getAssets(), assetName).build();
} catch (IOException e) {
Log.e("MainActivity", "loadGif error", e);
}
return null;
}
} |
Does it help if you reassign callback after changing drawable in one of the views?
|
Yes, it help, but it some cases (if there is many ImageViews and if there can be different drawables/bitmaps). public class GifImageView extends AppCompatImageView {
@Nullable
private GifDrawable mCurrentGifDrawable;
@Nullable
private MultiCallback mCurrentGifMultiCallback;
public GifImageView(Context context) {
super(context);
}
public GifImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GifImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void setImageResource(int resId) {
setGifDrawable(null);
super.setImageResource(resId);
}
@Override
public void setImageBitmap(Bitmap bm) {
setGifDrawable(null);
super.setImageBitmap(bm);
}
@Override
public void setImageIcon(@Nullable Icon icon) {
setGifDrawable(null);
super.setImageIcon(icon);
}
@Override
public void setImageURI(@Nullable Uri uri) {
setGifDrawable(null);
super.setImageURI(uri);
}
@Override
public void setImageDrawable(@Nullable Drawable newDrawable) {
if (newDrawable instanceof GifDrawable) {
setGifDrawable((GifDrawable) newDrawable);
} else {
setNonGifDrawable(newDrawable);
}
}
private void setNonGifDrawable(Drawable newDrawable) {
setGifDrawable(null);
if (newDrawable != null) {
super.setImageDrawable(newDrawable);
}
}
private void setGifDrawable(@Nullable GifDrawable newGifDrawable) {
if (newGifDrawable == mCurrentGifDrawable) {
return; //early exit
}
GifDrawable previousGifDrawable = mCurrentGifDrawable;
MultiCallback previousGifMultiCallback = mCurrentGifMultiCallback;
Drawable.Callback originalCallback = newGifDrawable != null ? newGifDrawable.getCallback() : null;
/**When we set new drawable to ImageView then our MultiCallback callback in gif drawable will be reset ot null (see android.widget.ImageView#updateDrawable)
* This leads to gif animation getting stuck.
* To fix it we hold current gif and after call 'super.setImageDrawable(Drawable drawable) we need set to this gif MultiCallback which was lost
*/
super.setImageDrawable(newGifDrawable);
if (previousGifDrawable != null) {
previousGifMultiCallback.removeView(this);
//reset callback to gif that has been changed after call super.setImageDrawable(..)
previousGifDrawable.setCallback(previousGifMultiCallback);
}
mCurrentGifDrawable = newGifDrawable;
if (mCurrentGifDrawable != null) {
if (originalCallback instanceof MultiCallback) {
mCurrentGifMultiCallback = ((MultiCallback) originalCallback);
} else {
mCurrentGifMultiCallback = new MultiCallback();
if (originalCallback != null && originalCallback != this) {
mCurrentGifMultiCallback.addView(originalCallback);
}
}
mCurrentGifMultiCallback.addView(this);
//we have to set MultiCallback to new GifDrawable because afer call
mCurrentGifDrawable.setCallback(mCurrentGifMultiCallback);
} else {
mCurrentGifMultiCallback = null;
}
}
} Perhaps you can find a better solution, if it possable (I understand that android API prevents to fix it properly because some methods is final, some private etc...). Anyway it make sense add some description to README if this bug can't be fixed properly. |
I think there are 2 groups of solutions:
I've added appropriate information to readme with reference to this thread. |
I want to play one GifDrawable in several ImageViews.
I do the following:
Everything is ok!
Then i want to change drawable in imageViewA only (in imageViewB GifDrawable remains the same)
I do the following:
After that gif animation in imageViewB getting stuck after few frames. But I don't change enything in imageViewB and in myGifDrawable!
See this video:
https://drive.google.com/open?id=1DU54uaQDFvilBWiE3Y1zngBx4pViFN0C
the problem is that: when i call 'imageViewA.setImageDrawable(newDrawable);' imageViewA still hold myGifDrawable which is also set in imageViewB.
android.widget.ImageView#setImageDrawable call method 'updateDrawable' that set to myGifDrawable 'null' callback, so after that moment animation in imageViewB getting stuck because myGifDrawable lost myMultiCallback
see code from android.widget.ImageView:
call 'mDrawable.setCallback(null);' in updateDrawable broke animation
The text was updated successfully, but these errors were encountered: