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
Related
On the android material design principles page, one of the examples shows a FAB expanding into a new full screen. (Under "Full Screen")
http://www.google.com/design/spec/components/buttons-floating-action-button.html#buttons-floating-action-button-transitions
I've tried to implement the same effect in my app, but with little success.
I managed to create a FAB that expands into a view using this code as reference: https://gist.github.com/chris95x8/882b5c5d0aa2096236ba.
It worked, but I was wondering whether I could apply the same effect to an activity transition. I've tried looking it up and playing with it myself but could not find anything that might work.
I know I could make the FAB expand into a Fragment and not a whole new activity, but I'm not sure if that's what being done, and whether that's optimal or not.
And so my question is, is there a way to implement the fab-expanding reveal effect as an activity transition, or is it supposed to just reveal a new fragment?
I am developing an app which expands a FloatingActionButton into a new Activity. I'm not sure that if you like my implementation, but please see pictures at first:
So the first picture shows MainActivity and the last one shows SecondActivity, which is "expanded" from FAB.
Now, I want to mention that I'm not actually expanding a FAB into a new Activity but I can let user feel that the new page is expanded from that FAB, and I think that's enough for both developers and users.
Here's implementation:
Preparation:
A FloatingActionButton of course,
Visit https://github.com/kyze8439690/RevealLayout and import this library to your project. It is used to play reveal animation. It has a custom BakedBezierInterpolator to control reveal animation and make it material-styled.
Steps:
create activity_main.xml like this:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--Your main content here-->
<RevealLayout
android:id="#+id/reveal_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible">
<View
android:id="#+id/reveal_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"/>
</RevealLayout>
</FrameLayout>
find Views:
mRevealLayout = (RevealLayout) findViewById(R.id.reveal_layout);
mRevealView = findViewById(R.id.reveal_view);
expand when user clicks FAB:
mFab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mFab.setClickable(false); // Avoid naughty guys clicking FAB again and again...
int[] location = new int[2];
mFab.getLocationOnScreen(location);
location[0] += mFab.getWidth() / 2;
location[1] += mFab.getHeight() / 2;
final Intent intent = new Intent(MainActivity.this, SecondActivity.class);
mRevealView.setVisibility(View.VISIBLE);
mRevealLayout.setVisibility(View.VISIBLE);
mRevealLayout.show(location[0], location[1]); // Expand from center of FAB. Actually, it just plays reveal animation.
mFab.postDelayed(new Runnable() {
#Override
public void run() {
startActivity(intent);
/**
* Without using R.anim.hold, the screen will flash because of transition
* of Activities.
*/
overridePendingTransition(0, R.anim.hold);
}
}, 600); // 600 is default duration of reveal animation in RevealLayout
mFab.postDelayed(new Runnable() {
#Override
public void run() {
mFab.setClickable(true);
mRevealLayout.setVisibility(View.INVISIBLE);
mViewToReveal.setVisibility(View.INVISIBLE);
}
}, 960); // Or some numbers larger than 600.
}
});
And here is hold.xml in res/anim:
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="960" <!-- Enough-large time is OK -->
android:fromXDelta="0%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="0%"/>
</set>
That's all.
Improvements:
RevealLayout has a bug(plays rectangular instead of circular reveal animation) for devices under API 17(Android 4.2), you can add these lines in constructor of it:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
If your SecondActivity contains complicated contents, a simple View used as reveal_view in the layout.xml isn't enough/perfect. You can include the second layout inside the RevealLayout reveal_layout. It seems wasteful and hard to control if the second layout won't appear same at every time. But for me, it will. So you can make other improvements if you should.
If you want to implement totally same animation shown in Material Design Guide, you can set layout_height of the RevealLayout into a specific number instead of match_parent. After expanding animation ends(or some time after the animation plays, which should make the whole process of animation smoothly), then you can animate translationY. The important point is, just cheat users visually by controlling animation duration.
Finally, this is my own experience/attempt and I'm a beginner in developing Android apps. If there are any mistakes/further improvements, please leave comments/edit my answer. Thank you.
I made a custom activity, based on this question Circular reveal transition for new activity , that handle the CircularRevealAnimation and his reverse effect when the activity finish:
public class RevealActivity extends AppCompatActivity {
private View revealView;
public static final String REVEAL_X="REVEAL_X";
public static final String REVEAL_Y="REVEAL_Y";
public void showRevealEffect(Bundle savedInstanceState, final View rootView) {
revealView=rootView;
if (savedInstanceState == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
rootView.setVisibility(View.INVISIBLE);
ViewTreeObserver viewTreeObserver = rootView.getViewTreeObserver();
if(viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
circularRevealActivity(rootView);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
}
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void circularRevealActivity(View rootView) {
int cx = getIntent().getIntExtra(REVEAL_X, 0);
int cy = getIntent().getIntExtra(REVEAL_Y, 0);
float finalRadius = Math.max(rootView.getWidth(), rootView.getHeight());
// create the animator for this view (the start radius is zero)
Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootView, cx, cy, 0, finalRadius);
circularReveal.setDuration(400);
// make the view visible and start the animation
rootView.setVisibility(View.VISIBLE);
circularReveal.start();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: onBackPressed();break;
return super.onOptionsItemSelected(item);
}
}
#Override
public void onBackPressed() {
destroyActivity(revealView);
}
private void destroyActivity(View rootView) {
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)
destroyCircularRevealActivity(rootView);
else
finish();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void destroyCircularRevealActivity(final View rootView) {
int cx = getIntent().getIntExtra(REVEAL_X, 0);
int cy = getIntent().getIntExtra(REVEAL_Y, 0);
float finalRadius = Math.max(rootView.getWidth(), rootView.getHeight());
// create the animator for this view (the start radius is zero)
Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootView, cx, cy, finalRadius, 0);
circularReveal.setDuration(400);
circularReveal.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {
}
#Override
public void onAnimationEnd(Animator animator) {
rootView.setVisibility(View.INVISIBLE);
finishAfterTransition();
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
// make the view visible and start the animation
rootView.setVisibility(View.VISIBLE);
circularReveal.start();
}
}
You can extend this with your own activity and call in your onCreate the method 'showRevealEffect' like this:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_activity_layout);
//your code
View root= findViewById(R.id.your_root_id);
showRevealEffect(savedInstanceState, root);
}
You also have to use a transparent theme like this one:
<style name="Theme.Transparent" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">#android:color/transparent</item>
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="colorControlNormal">#android:color/white</item>
</style>
In the end, to launch this activity you should pass via extra the coordinates where the animation should start:
int[] location = new int[2];
fab.getLocationOnScreen(location);
Intent intent = new Intent(this, YourRevealActivity.class);
intent.putExtra(SearchActivity.REVEAL_X, location[0]);
intent.putExtra(SearchActivity.REVEAL_Y, location[1]);
startActivity(intent);
you can use this lib [https://github.com/sergiocasero/RevealFAB][1]
[1]: https://github.com/sergiocasero/RevealFAB 3rd party its easy and simple to use
Add to your layout
<RelativeLayout...>
<android.support.design.widget.CoordinatorLayout...>
<!-- YOUR CONTENT -->
</android.support.design.widget.CoordinatorLayout>
<com.sergiocasero.revealfab.RevealFAB
android:id="#+id/reveal_fab"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:fab_color="#color/colorAccent"
app:fab_icon="#drawable/ic_add_white_24dp"
app:reveal_color="#color/colorAccent" />
</RelativeLayout>
Important: This component goes above your content. You can use Coordinator, LinearLayout... or another Relative layout if you want :)
As you can see, you have 3 custom attributes for customizing colors and icon
Setting information about intent:
revealFAB = (RevealFAB) findViewById(R.id.reveal_fab);
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
revealFAB.setIntent(intent);
revealFAB.setOnClickListener(new RevealFAB.OnClickListener() {
#Override
public void onClick(RevealFAB button, View v) {
button.startActivityWithAnimation();
}
});
Don't forget call onResume() method!
#Override
protected void onResume() {
super.onResume();
revealFAB.onResume();
}
Someone investigated the implementation of transition between activities from Plaid. Her example were published via https://github.com/hujiaweibujidao/FabDialogMorph.
Briefly speaking, she transits two activities with:
The FAB as the shared element.
The layout in the target activity with the same android:transitionName as the FAB.
To smooth the animation, MorphDrawable (extended from Drawable) and MorphTransition (extended from ChangeBounds) are implemented and applied.
I'm making an app just to check out some of the new L APIs, and I'm particularly interested in the FloatingActionButton and the Palette colour generator. So I set a full screen ImageView to display an HTC wallpaper, I then extract one of the colours with Palette and set it as the background colour of my FAB. However, doing this removes the Outline from my FAB, so it's no longer circular.
I would like to know how, if possible, to stop the Palette overriding the Outline.
The Palette AsyncListener:
MyActivity.java
Palette.generateAsync(BitmapFactory.decodeResource(getResources(),R.drawable.wallpapers_07),
new Palette.PaletteAsyncListener() {
#Override
public void onGenerated(Palette palette) {
PaletteItem item = palette.getLightVibrantColor();
ImageButton button = (ImageButton) findViewById(R.id.fab);
if (item != null) {
button.setBackgroundColor(item.getRgb());
}
}
});
Outline in MyActivity.java
int size = getResources().getDimensionPixelSize(R.dimen.fab_size);
Outline outline = new Outline();
outline.setOval(0, 0, size, size);
findViewById(R.id.fab).setOutline(outline);
FAB background drawable
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight">
<item>
<shape android:shape="oval">
<solid android:color="?android:colorAccent"/>
</shape>
</item>
You are replacing the background drawable (RippleDrawable) with a color (ColorDrawable). Instead, you want to change the color of the shape (GradientDrawable) contained with the background drawable.
Try changing the color filter instead:
findViewById(R.id.fab).getBackgroundDrawable().setColorFilter(
new PorterDuffColorFilter(item.getRgb(), Mode.SRC_IN));
If you're changing the color often, you can just cache the color filter object and use setColor(int) followed by findViewById(R.id.fab).invalidate() to update the view.
I have an imageview with a default image. Now when I try to click it, I want it to animate that displays 4 frames of images. How could I accomplish this, I tried a simpler approach (more of a dumb approach) by changing the imageresource 4 times, as expected the image changes so fast that the animating image effect is not visible. Any ideas?
I tried this approach:
Gem = (ImageView)v;
Gem.setImageResource(com.example.gems.R.drawable.bd1);
Gem.postDelayed(new Runnable() {
public void run() {
Gem.setImageResource(com.example.gems.R.drawable.bd2);
Gem.postDelayed(new Runnable() {
public void run() {
Gem.setImageResource(com.example.gems.R.drawable.bd3);
Gem.postDelayed(new Runnable() {
public void run() {
Gem.setImageResource(com.example.gems.R.drawable.bd4);
Gem.postDelayed(new Runnable() {
public void run() {
}
}, 500);
}
}, 500);
}
}, 500);
}
}, 500);
It worked, but is there a better way to this without coding too much lines? I have 25 kinds of images and each image has 4 frames.
EDIT:
I tried using xml transition files:
Java file:
Resources res = this.getResources();
Gem = (ImageView)v;
TransitionDrawable transition;
transition = (TransitionDrawable)
res.getDrawable(R.drawable.blue_diamond_animation);
Gem.setImageDrawable(transition);
transition.startTransition(3000);
xml file:
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/bd1"></item>
<item android:drawable="#drawable/bd2"></item>
<item android:drawable="#drawable/bd3"></item>
<item android:drawable="#drawable/bd4"></item>
<item android:drawable="#drawable/invi"></item>
</transition>
This seems to work, but I don't want to draw this images in a transition. I want to change the background in a transition. I tried changing this android:drawable to android:drawable but it doesn't work.
It turns out that there is an exact class for this: AnimationDrawable
Basically, just add frames with the other pictures to use in the animation to the AnimationDrawable object and specify how long they should display using addFrame(Drawable frame, int duration)
Then set the ImageView to display whatever image it should start with and set the background to the AnimationDrawable you just made using setBackgroundDrawable(Animation)
Lastly, start the animation in the onClick listener
EDIT: FOR EXAMPLE
AnimationDrawable ad = new AnimationDrawable();
ad.addFrame(getResources().getDrawable(R.drawable.image1), 100);
ad.addFrame(getResources().getDrawable(R.drawable.image2), 500);
ad.addFrame(getResources().getDrawable(R.drawable.image3), 300);
ImageView iv = (ImageView) findViewById(R.id.img);
iv.setBackgroundDrawable(animation);
And then in your onClick listener, just call ad.start
It depends on what you are trying to accomplish. You have not given enough info to go on
The viewproperty animator is really the easiest thing to use have a look. It can also be used with older API's using nineoldandroids jar (google it)
http://developer.android.com/reference/android/view/ViewPropertyAnimator.html
The ObjectAnimator and ValueAnimator are also available similar and slightly more difficult to implement
http://developer.android.com/reference/android/animation/ObjectAnimator.html
http://developer.android.com/reference/android/animation/ValueAnimator.html
Have a look at the APIdemos in the sample packs there are a few examples mainly using the ValueAnimator.
http://developer.android.com/tools/samples/index.html
There are also Sprites to consider but it is basically a bitmap , you can user the timer obect to choreograph
This is my custom selector (StateListDrawable)
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="#drawable/common_cell_background" />
<item
android:state_pressed="true"
android:drawable="#drawable/common_cell_background_highlight" />
<item
android:state_focused="true"
android:drawable="#drawable/common_cell_background_highlight" />
<item
android:state_selected="true"
android:drawable="#drawable/common_cell_background_highlight" />
</selector>
Both, common_cell_background and common_cell_background_highlight are XML. Code below:
common_cell_background.xml
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/common_cell_background_bitmap"
android:tileMode="repeat"
android:dither="true">
</bitmap>
common_cell_background_highlight.xml
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/common_cell_background_bitmap_highlight"
android:tileMode="repeat"
android:dither="true">
</bitmap>
Bitmaps are also exactly the same. Highlight is just a little bit lighter and there is no other differences. Both bitmaps are PNG files.
Now I set
convertView.setBackgroundResource(R.drawable.list_item_background);
and here is the problem. My common_cell_background doesn't repeat, it's stretched. But what is suprising when I touch on cell of my list background changes to common_cell_background_highlight and guess what? Everything is fine, it's repeated like it should be. I have no idea where is the problem, why my background doesn't repeat while highlight does. Any thoughts?
This is the bug, it was fixed in ICS, see this answer: https://stackoverflow.com/a/7615120/1037294
Here is a workaround: https://stackoverflow.com/a/9500334/1037294
Note, that the workaround is only applicable for BitmapDrawable, for other types of drawables like StateListDrawable you'll need to do extra work. Here is what I use:
public static void fixBackgrndTileMode(View view, TileMode tileModeX, TileMode tileModeY) {
if (view != null) {
Drawable bg = view.getBackground();
if (bg instanceof BitmapDrawable) {
BitmapDrawable bmp = (BitmapDrawable) bg;
bmp.mutate(); // make sure that we aren't sharing state anymore
bmp.setTileModeXY(tileModeX, tileModeY);
}
else if (bg instanceof StateListDrawable) {
StateListDrawable stateDrwbl = (StateListDrawable) bg;
stateDrwbl.mutate(); // make sure that we aren't sharing state anymore
ConstantState constantState = stateDrwbl.getConstantState();
if (constantState instanceof DrawableContainerState) {
DrawableContainerState drwblContainerState = (DrawableContainerState)constantState;
final Drawable[] drawables = drwblContainerState.getChildren();
for (Drawable drwbl : drawables) {
if (drwbl instanceof BitmapDrawable)
((BitmapDrawable)drwbl).setTileModeXY(tileModeX, tileModeY);
}
}
}
}
}
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.