I'm trying to make an animated logo. It consists of two static images.
I would to like to achieve a cross-fading effect.
I've done it with the use of TransitionDrawable, set the crossFadeEnabled and everything looks nice.
The thing is that I need to be running in circle. How can it be achieved ?
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/image_expand">
<item android:drawable="#drawable/image_collapse">
</transition>
Resources res = mContext.getResources();
TransitionDrawable transition = (TransitionDrawable) res.getDrawable(R.drawable.expand_collapse);
ImageView image = (ImageView) findViewById(R.id.toggle_image);
image.setImageDrawable(transition);
This the code from google which runs perfectly.
The most importantn thing is that in needs to work under Android 1.6.
I managed to get the transition drawable to work via a handler method that reverses direction after the transition from drawable1 to drawable2. In my XML's :
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/background_animation1"/>
<item android:drawable="#drawable/background_animation2"/>
</transition>
The drawables are gradients :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners android:topLeftRadius = "10dp" android:topRightRadius="10dp" android:bottomLeftRadius="10dp" android:bottomRightRadius="10dp"/>
<gradient
android:startColor="#d200ff"
android:centerColor="#4e00ff"
android:endColor="#006cff"/>
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<size android:width="900dp" android:height="500dp"/>
<corners android:topLeftRadius = "10dp" android:topRightRadius="10dp" android:bottomLeftRadius="10dp" android:bottomRightRadius="10dp"/>
<gradient
android:startColor="#006cff"
android:centerColor="#ff6600"
android:endColor="#d200ff"/>
</shape>
In my initialisation code :
trans = (TransitionDrawable) getResources().getDrawable(R.drawable.transition);
trans.setCrossFadeEnabled(true);
backgroundimage.setImageDrawable(trans);
....
handlechange();
And the most important bit in the handler code; note the flag that is running globally. I got this running on Gingerbread and Kitkat;
void handlechange1()
{
Handler hand = new Handler();
hand.postDelayed(new Runnable()
{
#Override
public void run()
{
change();
}
private void change()
{
if (flag)
{
trans.startTransition(8000);
flag = false;
} else
{
trans.reverseTransition(8000);
flag = true;
}
handlechange1();
}
}, 8000);
}
There is not really a built-in way to do this. TransitionDrawable is not designed with an infinite looping animation function. The easiest recommendation would be to use an Animation (alpha, scale, translate, etc.) on a View containing just one of your Drawables if you can.
A fairly easy hack would be to add a Handler and callback to your custom View holding your TransitionDrawable. When the View is created, the Handler can be set to your transition interval. The View would also implement Handler.Callback and inside its handleMessage(Message) method, it would call reverseTransition(int) on your TransitionDrawable.
A rough example is below:
public class myView extends View implements Handler.Callback {
private Handler mHandler = new Handler(this);
private int mDelay = 1000; // Delay in milliseconds.
private Runnable mEvent = new Runnable() {
public void run() {
mHandler.removeCallbacks(mEvent);
mHandler.postDelayed(mEvent, mDelay);
Message message = mHandler.obtainMessage();
mHandler.sendMessage(message);
}
};
public View(Context context) {
super(context);
// Set your background TransitionDrawable.
setBackgroundDrawable(...);
}
public handleMessage(Message message) {
TransitionDrawable drawable = (TransitionDrawable) getBackgroundDrawable();
drawable.reverseTransition(mDelay);
}
public void start() {
mHandler.post(mEvent);
}
public void stop() {
mHandler.removeCallbacks(mEvent);
}
}
Call start() to start your continuous transition, stop() to stop it. It is a bit like creating your own animation, but it works in a pinch.
Related
I am new to android app development. I am trying to build 2048 game. I used images for tiles and loaded them for every swipe. At first i got memoryOutOfBounds Exception. Later i changed the largeHeap to true and it worked but is bit slow. So, i replaced the images with xml background and used buttons. But now my app is not working. onSwipe functions are not getting invoked. After swiping for many number of times then i am able to make a move that too woth incorrect background colours. Please help me figure this out.
Button b[][] =new Button[4][4];
GestureDetector gestureDetector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int idd[][] = {{R.id.block00, R.id.block01, R.id.block02, R.id.block03},
{R.id.block10, R.id.block11, R.id.block12, R.id.block13},
{R.id.block20, R.id.block21, R.id.block22, R.id.block23},
{R.id.block30, R.id.block31, R.id.block32, R.id.block33}};
int i,j;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
b[i][j] = (Button)findViewById(idd[i][j]);
start();
draw();
gestureDetector = new GestureDetector(MainActivity.this, MainActivity.this);
}
code for draw function
public void draw()
{
int i,j;
int img[] = {R.drawable.t0, R.drawable.t2, R.drawable.t4, R.drawable.t8, R.drawable.t16, R.drawable.t32, R.drawable.t64, R.drawable.t128, R.drawable.t256, R.drawable.t512, R.drawable.t1024, R.drawable.t2048, R.drawable.t4096, R.drawable.t8192, R.drawable.t16384, R.drawable.t32768, R.drawable.t65536, R.drawable.t131072};
for(i=0;i<4;i++)
for(j=0;j<4;j++)
{
if(grid[i][j]!=0)
b[i][j].setText(Integer.toString(grid[i][j]));
b[i][j].setBackgroundResource(img[log2(grid[i][j])]);
}
TextView tv=(TextView)findViewById(R.id.score);
TextView tv1=(TextView)findViewById(R.id.best);
tv.setText(Integer.toString(score));
tv1.setText(Integer.toString(best));
}
When i changed the xml object from button to ImageView and the code inside draw from setBackgroundResource to setImageResource, it is working fine.
one of the drawable xml files(t1024.xml)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<padding
android:left="4dp"
android:top="4dp"
android:right="4dp"
android:bottom="4dp"/>
<corners android:radius="6dp"/>
<solid android:color="#1a1d23"/>
All drawable resources contain the same code, i just changed the android:color attribute.
MainActivity.java
transition = new TransitionDrawable(new Drawable[]{
new ColorDrawable(Color.TRANSPARENT),
new BitmapDrawable(getResources(), BitmapFactory.decodeResource(getResources(), R.drawable.face1))
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
imageView.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.default_image, null));
}
imageView.setImageDrawable(transition);
transition.startTransition(3000);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
imageView.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.iv1_selector, null));
Log.d("LOG", "CALLED");
}
}
}, 3000);
iv1_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:enterFadeDuration="#android:integer/config_shortAnimTime"
android:exitFadeDuration="#android:integer/config_mediumAnimTime">
<item android:drawable="#drawable/iv_focused_drawable" android:state_focused="true" />
<item android:drawable="#drawable/iv_not_focused_drawable" android:state_focused="false" />
</selector>
I give a small animation, transition, to the imageView. After finishing the animation, imageView's background should be changed with a selector drawable inside a handler. However, for some reasons, its background is not applied, but when I click the imageView, it is applied. I have no idea on what's going on inside the handler. Any advice for this case?
This is how it looks like when the transition finished
But as you see this, I want to make it have the border around it. I don't understand why its background is not applied in the handler
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.
I'm trying to implement a smooth animation for my ProgressBar, but when I increase the time (30 seconds), the animation is no longer smooth.
Example with 5 seconds:
Example with 30 seconds:
My progress background:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<padding android:top="1dp" />
<solid android:color="#10444444" />
</shape>
</item>
<item>
<shape>
<padding android:top="1dp" />
<solid android:color="#20444444" />
</shape>
</item>
<item>
<shape>
<padding android:top="1dp" />
<solid android:color="#30444444" />
</shape>
</item>
<item android:id="#android:id/background">
<shape>
<solid android:color="#color/black_thirty" />
</shape>
</item>
<item android:id="#android:id/progress">
<clip>
<shape>
<solid android:color="#3500D0" />
</shape>
</clip>
</item>
</layer-list>
My progress layout:
<ProgressBar
android:id="#+id/pb_loading"
android:layout_width="match_parent"
android:layout_height="8dp"
android:indeterminate="false"
android:layout_centerInParent="true"
android:progress="100"
android:progressDrawable="#drawable/my_progress_bar" />
My animation method:
private void startAnimation(){
ProgressBar mProgressBar = (ProgressBar) findViewById(R.id.pb_loading);
ObjectAnimator progressAnimator = ObjectAnimator.ofInt(mProgressBar, "progress", 100, 0);
progressAnimator.setDuration(30000);
progressAnimator.setInterpolator(new LinearInterpolator());
progressAnimator.start();
}
If you change progress value each time by 1 (for example from 45 to 46) you won't see the animation. You'd better change progress by 100 points (or maybe other), for this you just need to multiply your max value by 100 and each progress value to 100 too. For example:
private void setProgressMax(ProgressBar pb, int max) {
pb.setMax(max * 100);
}
private void setProgressAnimate(ProgressBar pb, int progressTo)
{
ObjectAnimator animation = ObjectAnimator.ofInt(pb, "progress", pb.getProgress(), progressTo * 100);
animation.setDuration( 500);
animation.setAutoCancel( true);
animation.setInterpolator( new DecelerateInterpolator());
animation.start();
}
Because you are using ofInt you can only move at full integers. In other words, if you have a progress bar with a width of 1000 and a progress of 0 to 100 since you are moving at an integer pace you count 1, 2, 3, 4 which translates to 10px, 20px, 30px and 40px. Which explains the jaggedness you are seeing.
To correct this you have a few options. The first is to up your integers from 0 to someBigInt This will give the animator more numbers to work with.
ObjectAnimator progressAnimator = ObjectAnimator.ofInt(mProgressBar, "progress", 10000, 0);
The other option is to use ofFloat which does the same thing as ofInt but uses floating points instead of integers.
ObjectAnimator progressAnimator = ObjectAnimator.ofFloat(mProgressBar, "progress", 100.0, 0.0);
If you have Android N and above, you can use :
progressBar.setProgress(newProgress, true)
Docs:
Sets the current progress to the specified value, optionally animating
the visual position between the current and target values.
Animation does not affect the result of getProgress(), which will
return the target value immediately after this method is called.
https://developer.android.com/reference/android/widget/ProgressBar.html#setProgress(int,%20boolean)
Just set
android:max="1000"
and do
ObjectAnimator progressAnimator = ObjectAnimator.ofInt(mProgressBar, "progress", 1000, 0);
in this case you will animate on 1/1000 by each step which in 10 time smoothly when default 100 percent scale. and it looks much better
XML
<ProgressBar
android:id="#+id/progress_bar"
style="#style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="4dp"
android:indeterminate="false"
android:progress="0"
android:max="100"/>
JAVA
#BindView(R.id.progress_bar) ProgressBar progressBar;
ObjectAnimator.ofInt(progressBar, "progress", 79).start();
79
any number between 0 and 100 for this example
You can not use the ofFloat because the ProgressBar's progress attribute doesn't accept float values, only integer ones. That is why your ProgressBar stopped progressing after going with that solution.
As the others have said it, the correct way to do what you want is to set android:max to some big integer.
Sorry for reviving the thread but I feel like this had to be said.
Use the library may its help you give the time in which you want to fill the progress bar its very smooth with no lag but you little bit customize to use this.
Just add the dependency to your build.gradle:
compile 'com.carlosmuvi.segmentedprogressbar:library:0.2'
Next, add it to your layout
<com.carlosmuvi.segmentedprogressbar.SegmentedProgressBar
android:id="#+id/segmented_progressbar"
android:layout_width="match_parent"
android:layout_height="5dp"/>
Finally, customize it programatically and play it!
segmentedProgressBar = (SegmentedProgressBar) findViewById(R.id.segmented_progressbar);
// number of segments in your bar
segmentedProgressBar.setSegmentCount(7);
//empty segment color
segmentedProgressBar.setContainerColor(Color.BLUE);
//fill segment color
segmentedProgressBar.setFillColor(Color.GREEN);
//play next segment specifying its duration
segmentedProgressBar.playSegment(5000);
//pause segment
segmentedProgressBar.pause();
//set filled segments directly
segmentedProgressBar.setCompletedSegments(3);
GoToLibrary
here is snippet for my countdown timer using smooth animation you can modify as per your need please follow below :
private void setProgressBarValues() {
progressBarCircle.setMax((int) (timeCountInMilliSeconds / 10));
progressBarCircle.setProgress((int) (timeCountInMilliSeconds / 10));
Log.e("progres", "" + (timeCountInMilliSeconds / 10));
}
private void startCountDownTimer() {
smoothAnimation = ObjectAnimator.ofInt(progressBarCircle, "progress", progressBarCircle.getProgress(), progressBarCircle.getMax());
smoothAnimation.setDuration(500);
smoothAnimation.setInterpolator(new AccelerateInterpolator());
countDownTimer = new CountDownTimer(timeCountInMilliSeconds, 10) {
#Override
public void onTick(long millisUntilFinished) {
Log.e("getMax", "" + progressBarCircle.getMax());
Log.e("getprogres", "" + progressBarCircle.getProgress());
textViewTime.setText(hmsTimeFormatter(millisUntilFinished));
progressBarCircle.setProgress((int) (timeCountInMilliSeconds / 10 - millisUntilFinished / 10));
}
#Override
public void onFinish() {
textViewTime.setText(hmsTimeFormatter(timeCountInMilliSeconds));
// call to initialize the progress bar values
setProgressBarValues();
// hiding the reset icon
buttonReset.setEnabled(false);
// making edit text editable
editTextMinute.setEnabled(true);
// changing the timer status to stopped
status = TimerStatus.STOPPED;
smoothAnimation.end();
}
}.start();
smoothAnimation.start();
countDownTimer.start();
}
Summary:
setMax and setProgress
setAnimation to show from progess to max values of progressbar
create a timer with call back of ~10 millisec
update progress in onTick i.e total - finished
You can make custom clas like this :
public class CustomProgressBar extends ProgressBar {
private static final long DEFAULT_DELAY = 500;
private static final long DEFAULT_DURATION = 1000;
public CustomProgressBar(Context context) {
super(context);
}
public CustomProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public synchronized void setProgress(float progress) {
super.setProgress((int) progress);
}
#Override
public synchronized void setProgress(int progress) {
super.setProgress(progress);
}
public void startLcpProgressAnim(int progressTo) {
startLcpProgressAnim(DEFAULT_DELAY, progressTo);
}
public void startLcpProgressAnim(long delay, int progressTo) {
startLcpProgressAnim(DEFAULT_DURATION, delay, progressTo);
}
public void startLcpProgressAnim(long duration, long delay, float progressTo) {
ObjectAnimator animation = ObjectAnimator.
ofFloat(this, "progress",
(float)this.getProgress(), progressTo);
animation.setDuration(duration);
animation.setStartDelay(delay);
animation.start();
}
}
Android N does provide a variant as setProgress(newProgress, true) which support animating the progress .
It will require a version check, though there is another method which you can directly use setProgressCompat(progress, true) regardless of version.
progressBar.setProgressCompat(progress, true)
In my android project I have an image that i want to rotate it and create animation for long time.
I can run program and it works fine. but I don't know how can I stop it. if you know please tell me.
Thanks
This is my code, I have a menu with two items, When I click on Start item, startAnimation function runs and when I click on Stop item, stopAnimation runs.:
public class Hypnagogic extends Activity
{
ImageView imView;
Animation an;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
imView = (ImageView)findViewById(R.id.imView);
}
private void startAnimation()
{
an = AnimationUtils.loadAnimation(this, R.anim.spin);
imView.startAnimation(an);
}
private void stopAnimation()
{
;//there is no any stop function!
}
}
also, in res/anim I put spin.xml that is my animation
<?xml version="1.0" encoding="utf-8"?>
<set>
<rotate
android:fromDegrees="0"
android:toDegrees="-18000"
android:pivotX="50%"
android:pivotY="50%"
android:duration="50000" />
</set>
EDIT: I see. Okay, try the clearAnimation() method associated with the View class. You can call it directly on your ImageView, I'm almost certain this should work for your case:
imView.clearAnimation();
What have you tried? AnimationDrawable has a stop() method. You need to post how you're creating/starting the animation.