Background TransitionDrawable not redrawing on some devices - android

I'm using a TransitionDrawable two go between two GradientDrawables, and I'm setting that as the background drawable of a Button. This works fine transitioning between two ColorDrawables, but when I try doing it with GradientDrawables I get some very strange results.
On my 2.3.5 device everything works peachy.
On my 4.2.2 device, the buttons just takes on the first gradient and don't redraw at all.
If I add a call to view.refreshDrawableState() or view.setBackgroundDrawable(drawable) after I've started the transition, it will refresh but things get even weirder:
some buttons work correctly
some buttons flicker at points during the transition
one button, for whatever reason, transitions to black instead of to the second gradient drawable
I am looping the transition with a recursive post to the view, but I don't think that's problem because I get the same problems when I don't do it.
Does anyone know what I can do to fix this? Here's the relevant code:
private static void highlightView(View view, Context context) {
TransitionDrawable drawable;
GradientDrawable d1 = new GradientDrawable(Orientation.TOP_BOTTOM, new int[] { HIGHLIGHT_COLOR_1, HIGHLIGHT_COLOR_G1 });
d1.setCornerRadius(3f);
GradientDrawable d2 = new GradientDrawable(Orientation.TOP_BOTTOM,
new int[] { HIGHLIGHT_COLOR_2, HIGHLIGHT_COLOR_G2 });
d2.setCornerRadius(3f);
drawable = new TransitionDrawable(new Drawable[] {
d1,
d2
});
view.setBackgroundDrawable(drawable);
drawable.setCrossFadeEnabled(true);
animateBackground(drawable, true, view);
}
private static void animateBackground(final TransitionDrawable drawable,
final boolean forward, final View view) {
if (view.getBackground() != drawable) return;
if (forward) {
drawable.startTransition(HIGHLIGHT_CYCLE / 2);
} else {
drawable.reverseTransition(HIGHLIGHT_CYCLE / 2);
}
view.postDelayed(new Runnable() {
#Override
public void run() {
animateBackground(drawable, !forward, view);
}
}, HIGHLIGHT_CYCLE / 2);
view.refreshDrawableState(); //without this, no transition occurs at all
}

Related

Colors "flowing" in Android background

I have an application and I want to set it's background just like the Instagram's login page, the one with that "gradient colors". The colors are "moving" and it creats a cool effect. This is what I've tried so far:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_take_one);
colorFul = (RelativeLayout) findViewById(R.id.background);
GradientDrawable gd = new GradientDrawable(GradientDrawable.Orientation.TL_BR, new int[] {a,b});
gd.setCornerRadius(0f);
colorFul.setBackgroundDrawable(gd);
for(int i = 0; i<6; i++){
if(a == 0xff000000){
a = 0xff00ffff;
b = 0xff444444;
}else if(a == 0xff00ffff){
a = 0xff888888;
b = 0xff00ff00;
}else if(a == 0xff888888){
a = 0xffcccccc;
b = 0xffff00ff;
}else if(a==0xffcccccc){
a = 0xffff0000;
b = 0xffffffff;
}else if(a==0xffff0000){
a = 0xffffffff;
b = 0xffffff00;
}
System.out.println("||");
SystemClock.sleep(1000);
System.out.println(">");
}
}
But this doesn't work. What happens is that the collors don't change, it remains black until it gets to the last if and then the background become white and yellow(that are, respectively, the collors 0xffffffff and 0xffffff00), in gradient shape.
So, how can I do it? I don't care how it's done. Whether it is with GradientDrawable or using an animated gif as background(which didn't work, the gif remained still) or any other way. Thanks.
(This is what I want, the gif isn't that good but you can see how the background is changing it's color)
You could programmatically create something that changes the level of gradient over time, but that would require some effort. Alternatively, you could use a TransitionDrawable to fade between different gradients, which should produce a similar effect.
This can be implemented as follows:
<?xml version="1.0" encoding="UTF-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<!-- use two gradients, extending from opposite sides of the page-->
<item android:drawable="#drawable/gradientLeft" />
<item android:drawable="#drawable/gradientRight" />
</transition>
Then, you can activate the transition in code as follows:
TransitionDrawable transition = (TransitionDrawable) myLayout.getBackground();
transition.startTransition(1500);
Obviously you'll probably need to spend some time to get it looking the way you want, but this would be far easier than hard-coding a transitional algorithm!
COMPLETE AND WORKING CODE BELOW
So, I got my idea implemented, and it looks awesome! I didn't expect it would look this nice, but it's really cool. I was bored so decided I would shove the whole thing into a function so it's easier for you (and others) to utilize.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView image = (ImageView) findViewById(R.id.background);
Drawable backgrounds[] = new Drawable[3];
backgrounds[0] = ContextCompat.getDrawable(this, R.drawable.gradient1);
backgrounds[1] = ContextCompat.getDrawable(this, R.drawable.gradient2);
backgrounds[2] = ContextCompat.getDrawable(this, R.drawable.gradient3);
Crossfade(image, backgrounds, 10000);
}
public void Crossfade(final ImageView image, final Drawable layers[], final int speedInMs) {
class BackgroundGradientThread implements Runnable {
Context mainContext;
TransitionDrawable crossFader;
boolean first = true;
BackgroundGradientThread(Context c) {
mainContext = c;
}
public void run() {
Handler mHandler = new Handler(mainContext.getMainLooper());
boolean reverse = false;
while (true) {
if (!reverse) {
for (int i = 0; i < layers.length - 1; i++) {
Drawable tLayers[] = new Drawable[2];
tLayers[0] = layers[i];
tLayers[1] = layers[i + 1];
final TransitionDrawable tCrossFader = new TransitionDrawable(tLayers);
tCrossFader.setCrossFadeEnabled(true);
Runnable transitionRunnable = new Runnable() {
#Override
public void run() {
image.setImageDrawable(tCrossFader);
tCrossFader.startTransition(speedInMs);
}
};
mHandler.post(transitionRunnable);
try {
Thread.sleep(speedInMs);
} catch (Exception e) {
}
}
reverse = true;
}
else if (reverse) {
for (int i = layers.length - 1; i > 0; i--) {
Drawable tLayers[] = new Drawable[2];
tLayers[0] = layers[i];
tLayers[1] = layers[i - 1];
final TransitionDrawable tCrossFader = new TransitionDrawable(tLayers);
tCrossFader.setCrossFadeEnabled(true);
Runnable transitionRunnable = new Runnable() {
#Override
public void run() {
image.setImageDrawable(tCrossFader);
tCrossFader.startTransition(speedInMs);
}
};
mHandler.post(transitionRunnable);
try {
Thread.sleep(speedInMs);
} catch (Exception e) {
}
}
reverse = false;
}
}
}
}
Thread backgroundThread = new Thread(new BackgroundGradientThread(this));
backgroundThread.start();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin" tools:context=".MainActivity">
<ImageView android:id="#+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/gradient2"
android:scaleType="fitXY"/>
<TextView android:text="Hello World!" android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
gradient1/2.jpg
The primary function here creates a new thread that will automatically begin shifting the two images back and forth. Once one full transition has completed, it will then begin to fade back into the original image. It creates a really smooth and flowy effect most of the time, but may look weird when combining some gradient drawables. The function looks like this:
public void Crossfade(final ImageView image, final Drawable layers[], final int speedInMs)
1) final ImageView image
The first parameter should be the image object that is going to represent your background.
2) final Drawable layers[]
The second parameter takes an array of drawables, which should be two images like the ones I showed above. Playing around with different gradient images is fun, and quite interesting to watch how colors "flow". Be careful with your image sizes, though, as I had initially used massive images that had crashed the app or stuttered terribly in the animation process.
3) final int speedInMs
The last parameter simply represents the time (in milliseconds) it will take to completely transition from the first drawable to the second.
Here's a GIF of the result of these two images crossfading with a speed of 5000ms (note: I couldn't put a 10 second GIF on here showing the whole transition, but it's quite smooth!):
Github Link To Repository
EDIT 2: I updated the Github and the function on here, but I just added in multi-drawable support so an array of any number of images can be added in and it will cycle through them chronologically, until reaching the end at which point it will reverse through and repeat. Sexy.

Displaying large png file using zoom from thumb view image

Hi I have successfully coded my app to display large png files from thumb view. When the thumb view is clicked the image enlarges to full screen. The problem I'm having is the app runs very slow due to a lack of memory. So I have made an attempt to save memory by using "notifyDrawable" however I am a noobie at this and I have probably gone about this the wrong way. I was wondering if anyone could suggest of a better way to save memory and a way to make my app run more smoothly. Here is the code I've used:
final View thumb1View = findViewById(R.id.thumb_button_1);
thumb1View.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
zoomImageFromThumb(thumb1View, R.drawable.page1);
}
#Override
public void setImageDrawable(Drawable drawable) {
final Drawable previousDrawable = getDrawable();
super.zoomImageFromThumb(drawable);
notifyDrawable(drawable, true);
notifyDrawable(previousDrawable, false);
}
private static void notifyDrawable(Drawable drawable, final boolean isDisplayed) {
if (drawable instanceof English_Infants) {
// The drawable is a CountingBitmapDrawable, so notify it
((English_Infants) drawable).setIsDisplayed(isDisplayed);
} else if (drawable instanceof LayerDrawable) {
// The drawable is a LayerDrawable, so recurse on each layer
LayerDrawable layerDrawable = (LayerDrawable) drawable;
for (int i = 0, z = layerDrawable.getNumberOfLayers(); i < z; i++) {
notifyDrawable(layerDrawable.getDrawable(i), isDisplayed);
}
}
}
});

android transition background of view from one color to another

I have a view with a black background with transparency: #DD000000
What I want is that the view, in some circumstances, transitions from #223333FF back to the first color.
//lastApp.setBackgroundColor(getResources().getColor(R.color.semitransparent));
ColorDrawable[] color = {new ColorDrawable(Color.BLUE), new ColorDrawable(Color.BLACK)};
TransitionDrawable trans = new TransitionDrawable(color);
int sdk = android.os.Build.VERSION.SDK_INT;
if(sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
lastApp.setBackgroundDrawable(trans);
} else {
lastApp.setBackground(trans);
}
trans.startTransition(300);
This code works, but the colors are not the ones I want.
If I use the next line I believe it creates two Black colors, because I don't see any blue, and the transparency is lost.
ColorDrawable[] color = {new ColorDrawable(getResources().getColor(R.color.blue)), new ColorDrawable(getResources().getColor(R.color.black_overlay))};
My next attempt was using resources without the alpha: #DD000000 -> #000000; #223333FF -> #3333FF
Here the transition worked, but it wasn't what I wanted as there was no transparency.
The next two attemps were simply trying something new, as I want to use color resources.
With the next line I get a black without transparency.
ColorDrawable[] color = {new ColorDrawable(0x223333FF), new ColorDrawable(0xDD000000)};
This results in a final blueish color, instead of transparent black
ColorDrawable[] color = {new ColorDrawable(0x223333FF), new ColorDrawable(0x33000000)};
I have also tried, with no effect:
color[1].setAlpha(50);
So, what am I doing wrong? Or is there a better way of accomplishing this?
You can do it with ArgbEvaluator
ObjectAnimator animator = ObjectAnimator.ofInt(yourView, "backgroundColor", Color.RED, Color.BLUE).setDuration(3000);
animator.setEvaluator(new ArgbEvaluator());
animator.start();
ObjectAnimator works from android 3.0 (sdk 11). For previous versions i did next:
private void init(int value){
int alpha = value*0x1000000;
ColorDrawable cl1= new
ColorDrawable(mContext.getResources().getColor(R.color.transtion_item1)
+alpha);
ColorDrawable cl2= new
ColorDrawable(mContext.getResources().getColor(R.color.transtion_item2)
+alpha);
TransitionDrawable transition = new TransitionDrawable( new Drawable[]{cl1,cl2});
if(Build.VERSION.SDK_INT<16)
v.setBackgroundDrawable(transition);
else
v.setBackground(transition);
mTransitionCount=0;
startCyclicTransition(transition);
}
private void startCyclicTransition(final TransitionDrawable trans){
if (mTransitionCount >=0) {
if(mTransitionCount%2==0) {
trans.startTransition(2000);
}else {
trans.reverseTransition(2000);
}
mTransitionCount++;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
startCyclicTransition(trans);
}
}, 2000);
}
}

Gridview randomly failing to update properly on first load, remains incorrect until it's destroyed

I have a 2x27 gridview that's holding white imageviews. I then apply a colorfilter to the imageviews, and every cycle invalidate them. The end result is some animated colors moving around the gridview.
I'm having problems with the gridview randomly showing a white square, instead of the black I initalize everything to. Then, as the colors change every imageview updates, except for the ones that failed to load properly the first time. They always remain white.
Sometimes this doesn't happen. Sometimes it's 1 square, sometimes it's 4 squares.
If I rotate my screen, the stuck white ones go away and everything works. They never come back no matter how many times I rotate/leave the activity and return.
This is me updating the colorfilters. I've checked in here that every grid is getting updated, nothing remains white.
public void updateColors() {
Runnable runnable1 = new Runnable() {
#Override
public void run() {
int r = 0;
int g = 0;
int b = 0;
int y = 0;
while(keepRunning == true){
y = 0;
for(int x=0;x<Consts.VIEWCOUNT;x++){
g = Buffers.offLineBuffer_[y++];
r = Buffers.offLineBuffer_[y++];
b = Buffers.offLineBuffer_[y++];
Drawable myIcon = getResources().getDrawable( ImageAdapter.mThumbIds[x]);
myIcon.setColorFilter(new PorterDuffColorFilter(Color.rgb(r, g, b),PorterDuff.Mode.MULTIPLY));
}
try {
Thread.sleep(55);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
#Override
public void run() {
gridView.invalidateViews();
}
});
}
}
};
new Thread(runnable1).start();
}
This is my adapter
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(33, 33));
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(mThumbIds[position]);
return imageView;
}
mThumbsID is just a list of ID's to drawables like below (I shortened it)
public static Integer[] mThumbIdsTemp = {
R.drawable.image1,
R.drawable.image2,
R.drawable.image3,
R.drawable.image4,
}
It just started doing this one day, I haven't been able to track it back to what is causing it, or why it's only ever on the first time it's loaded, but will remain until it's destroyed via rotating the screen.
I've lost over a day trying to track this down, and I'm lost as to what might be causing it.
Any ideas?

Set a gradient background

In my Activity I need to change an ImageView background using a gradient, so I use an image with a transparent area, changing its background when I need. Here's some code:
private static View myImage;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myActivityLayout);
myImage = findViewById(R.id.myImageID);
}
[...]
private void myImageUpdate() {
GradientDrawable gradient;
int[] colors = {0xFF00FF00, 0xFF0000FF};
// I make some changes to these colors..
gradient = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, colors);
myImage.setBackgroundDrawable(gradient);
}
Now, the problem is:
If I call myImageUpdate() within onCreate() method, everything works fine.
If I call myImageUpdate() from another part of the code (like an onClick callback), I can't set my backgroud!
* UPDATE *
Guys, this code is fine... I was calling my method in a wrong (not directly reachable) line! My apologies...
I don't think this will fix it since you said myImageUpdate gets called within onClick... but try this..
runOnUiThread(new Runnable() {
public void run() {
myImage.setBackgroundDrawable(gradient);
}
});
you might have to make gradient variable final..
final GradientDrawable gradient = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, colors);
Try myImage.invalidate(). This will force the system to redraw the view.

Categories

Resources