I'm using the KenBurnsView in the login screen of my App to show several images in the background. The thing is that this images change too abruptly. Isn't a way to implement a fadein/fadeout effect when changing the transitions from one image to another, hooking somewhere in the view API?
This is the code that I'm using to implement the transitions.
private void setupAnimationBackground() {
mBackgroundImageView.setTransitionListener(new KenBurnsView.TransitionListener() {
#DrawableRes int[] mResources = new int[]{
R.drawable.splash1, R.drawable.splash2, R.drawable.splash3,
R.drawable.splash4, R.drawable.splash5, R.drawable.splash6
};
int mIndex = 0;
#Override
public void onTransitionStart(Transition transition) {
mIndex = (mIndex == mResources.length - 1) ? 0 : mIndex + 1;
}
#Override
public void onTransitionEnd(Transition transition) {
mBackgroundImageView.setImageDrawable(ContextCompat.getDrawable(getContext(), mResources[mIndex]));
}
});
}
mBackgroundImageView is a KenBurnsView. I have the images resources in my drawable folder. As you can see I store the references in a resource int array.
Sorry, nope. You need to have two KenBurnsViews one overlapping on top of the other and you handle the crossfading yourself.
Related
I'm extremely new to Android programming. Right now I'm building a very simple app so i can get the hang of things.
Basically i have:
- Background
- ImageView
- ImageButton.
Every time I click the ImageButton, it cycles through a list of images I have stored in an array. However I'm ending up skipping a lot of frames.
private static ImageButton button;
private static ImageView current;
private int index;
int[] images = {R.drawable.image1, R.drawable.image2};
public void buttonClick() {
current = (ImageView)findViewById(R.id.imageView);
button = (ImageButton)findViewById(R.id.imageButton);
button.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
current.setImageResource(images[index]);
if(index == (images.length - 1)) {
index = 0;
}
else {
index++;
}
}
}
);
}
Any idea what is causing it to skip frames, and what I can do to fix it? Is it because my image files are too big? They are about 788kb each and 1920x1120.
Thank you.
You must be doing it off the ui thread.
Images/Bitmaps should be efficiently loaded.
Refer example on official page: https://developer.android.com/training/displaying-bitmaps/index.html
Yes. The size is the image is probably causing the skipping of frames. Try using smaller images to verify that your images are the problem.
I'm using the TransitionManager.beginDelayedTransition outlined here
to animate two views swapping positions within a RelativeLayout. I do this by simply swapping the RelativeLayout.LayoutParams of two the two views.
My question is how do I monitor the animation that is automatically created and executed by TransitionManager without having to create my own custom Transitions. I need to detect when the animation has ended so that I can make a change to the views that have been swapped.
Below is the method that swaps the two views. CollageCanvasAperture is an extension of View and mApertureGroup is the RelativeLayout that holds these views.
private void shuffle(int fromApertureInd, int toApertureInd) {
final CollageCanvasAperture fromV = (CollageCanvasAperture) mApertureGroup.getChildAt(fromApertureInd);
final CollageCanvasAperture toV = (CollageCanvasAperture) mApertureGroup.getChildAt(toApertureInd);
if (null == fromV || null == toV) {
return;
}
TransitionManager.beginDelayedTransition(mApertureGroup);
RelativeLayout.LayoutParams fromLP = (RelativeLayout.LayoutParams) fromV.getLayoutParams();
RelativeLayout.LayoutParams toLP = (RelativeLayout.LayoutParams) toV.getLayoutParams();
fromV.setLayoutParams(toLP);
toV.setLayoutParams(fromLP);
}
I've done a few hours of searching on here and combing through the TransitionManager code but can't see how to detect changes. I'd prefer to be able to detect the animation end within the CollageCanvasAperture but can't see any relevant listeners to apply.
I guess I could provide the view with the destination LayoutParams before the animation is executed and then the view can listen for size & location changes until they match...?
So it turned out it's quite straightforward to add a listener to these 'automatic' transitions.
Instead of using: TransitionManager.beginDelayedTransition(mApertureGroup);
you need to work out which automatic Transition is being using when you invoke beginDelayedTransition and call TransitionManager.go() instead.
My view transitions were using the ChangeBounds Transition. Once that was know all I had to do was:
ChangeBounds mySwapTransition = new ChangeBounds();
mySwapTransition.addListener(new Transition.TransitionListener() {
#Override
public void onTransitionStart(Transition transition) { }
#Override
public void onTransitionEnd(Transition transition) { }
#Override
public void onTransitionCancel(Transition transition) {}
#Override
public void onTransitionPause(Transition transition) { }
#Override
public void onTransitionResume(Transition transition) { }
});
TransitionManager.go(new Scene(mApertureGroup), mySwapTransition);
So in my android app I have a HorizontalScrollView that will display images. All images are downloaded an added to the View before it is avalible to the user. However when it does apear I want each image to animate in seperatly. Ive tried a LayoutTransition object attached to my layout with this code to show the views:
transition = new LayoutTransition();
transition.setStagger(LayoutTransition.APPEARING, 500);
transition.setDuration(1000);
transition.setAnimateParentHierarchy(true);
for (int i = 0; i < mGallery.getChildCount(); i++) {
mGallery.getChildAt(i).setVisibility(View.VISIBLE);
transition.showChild(mGallery, mGallery.getChildAt(i));
}
I have also tried this method using the the AnimationEndListener, and a recursive animateView() method
private void animateView(final int index) {
if (mGallery.getChildAt(index) == null)
return;
final View child = mGallery.getChildAt(index);
final Animation an = AnimationUtils.loadAnimation(mRootView.getContext(), R.anim.slideup);
AnimationEndListener listener = new AnimationEndListener() {
#Override
public void onAnimationEnd(Animation animation) {
animateView(index + 1);
}
};
an.setAnimationListener(listener);
child.setAnimation(an);
child.setVisibility(View.VISIBLE);
an.start();
}
The first method is preferable however it always animates in all my images at the same time. The second method kind of works, however it will apply the animation to the first view and the all subsequent views will appear in sequence without the animation playing.
Background
I'm trying to switch my alternative "App Manager" app from ActionBarSherlock library to the support library Google has created, since it gets more updates (ActionBarSherlock is no longer being developed, link here ) and I think it should cover a lot of functionality.
The problem
All went well (or so it seems), except for a class named ICSLinearLayout on ActionBarSherlock I've used to show dividers on, that is now called LinearLayoutICS .
It just doesn't show the dividers:
Note: before you ask "why don't you just use a GridView?", here's the reason, and also this, in case I'd ever want to add headers.
The code
The code is about the same as I've used for ActionBarSherlock:
rowLayout=new LinearLayoutICS(_context,null);
rowLayout.setMeasureWithLargestChildEnabled(true);
rowLayout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
rowLayout.setDividerDrawable(_context.getResources().getDrawable(R.drawable.list_divider_holo_dark));
rowLayout.setOrientation(LinearLayout.HORIZONTAL);
... // add views, layout params, etc...
The question
How can I use this class in order to support showing dividers on all supported OS versions of the support library?
What is wrong with the code I've written?
OK, it seems setShowDividers and setDividerDrawable cannot be used because LinearLayoutICS doesn't have them .
Not only that, but Lint didn't warn me about it being used.
So, what I ended up with is copying LinearLayoutICS code (from here, hope it's the latest version) and some of the original LinearLayout code, to make something that does work. I hope it doesn't have any bugs.
Long live open source ... :)
Sadly setMeasureWithLargestChildEnabled isn't available for old APIs, so I think the ActionBarSherlock way is still better in case that's something you wish to use.
EDIT: the setMeasureWithLargestChildEnabled method doesn't work on ActionBarSherlock.
Here's the code, for those who wish to use. I hope next time the library gets updated, I will remember to check this issue again.
public class LinearLayoutICS extends LinearLayout
{
private Drawable mDivider;
private int mDividerWidth,mDividerHeight;
private int mShowDividers;
private int mDividerPadding;
public LinearLayoutICS(final Context context,final AttributeSet attrs)
{
super(context,attrs);
// the R is from "android.support.v7.appcompat.R" .
final TypedArray a=context.obtainStyledAttributes(attrs,R.styleable.LinearLayoutICS);
mDivider=a.getDrawable(R.styleable.LinearLayoutICS_divider);
if(mDivider!=null)
{
mDividerWidth=mDivider.getIntrinsicWidth();
mDividerHeight=mDivider.getIntrinsicHeight();
}
else mDividerHeight=mDividerWidth=0;
mShowDividers=a.getInt(R.styleable.LinearLayoutICS_showDividers,SHOW_DIVIDER_NONE);
mDividerPadding=a.getDimensionPixelSize(R.styleable.LinearLayoutICS_dividerPadding,0);
a.recycle();
setWillNotDraw(mDivider==null);
}
#Override
protected void onDraw(final Canvas canvas)
{
if(getOrientation()==VERTICAL)
drawDividersVertical(canvas);
else drawDividersHorizontal(canvas);
}
#Override
protected void measureChildWithMargins(final View child,final int parentWidthMeasureSpec,final int widthUsed,final int parentHeightMeasureSpec,final int heightUsed)
{
if(mDivider!=null)
{
final int childIndex=indexOfChild(child);
final int count=getChildCount();
final LayoutParams params=(LayoutParams)child.getLayoutParams();
// To display the dividers in-between the child views, we modify their margins
// to create space.
if(getOrientation()==VERTICAL)
{
if(hasDividerBeforeChildAt(childIndex))
params.topMargin=mDividerHeight;
else if(childIndex==count-1&&hasDividerBeforeChildAt(count))
params.bottomMargin=mDividerHeight;
}
else if(hasDividerBeforeChildAt(childIndex))
params.leftMargin=mDividerWidth;
else if(childIndex==count-1&&hasDividerBeforeChildAt(count))
params.rightMargin=mDividerWidth;
}
super.measureChildWithMargins(child,parentWidthMeasureSpec,widthUsed,parentHeightMeasureSpec,heightUsed);
}
void drawDividersVertical(final Canvas canvas)
{
final int count=getChildCount();
for(int i=0;i<count;i++)
{
final View child=getChildAt(i);
if(child!=null&&child.getVisibility()!=GONE&&hasDividerBeforeChildAt(i))
{
final LayoutParams lp=(LayoutParams)child.getLayoutParams();
drawHorizontalDivider(canvas,child.getTop()-lp.topMargin);
}
}
if(hasDividerBeforeChildAt(count))
{
final View child=getChildAt(count-1);
int bottom=0;
if(child==null)
bottom=getHeight()-getPaddingBottom()-mDividerHeight;
else bottom=child.getBottom();
drawHorizontalDivider(canvas,bottom);
}
}
void drawDividersHorizontal(final Canvas canvas)
{
final int count=getChildCount();
for(int i=0;i<count;i++)
{
final View child=getChildAt(i);
if(child!=null&&child.getVisibility()!=GONE&&hasDividerBeforeChildAt(i))
{
final LayoutParams lp=(LayoutParams)child.getLayoutParams();
drawVerticalDivider(canvas,child.getLeft()-lp.leftMargin);
}
}
if(hasDividerBeforeChildAt(count))
{
final View child=getChildAt(count-1);
int right=0;
if(child==null)
right=getWidth()-getPaddingRight()-mDividerWidth;
else right=child.getRight();
drawVerticalDivider(canvas,right);
}
}
void drawHorizontalDivider(final Canvas canvas,final int top)
{
mDivider.setBounds(getPaddingLeft()+mDividerPadding,top,getWidth()-getPaddingRight()-mDividerPadding,top+mDividerHeight);
mDivider.draw(canvas);
}
void drawVerticalDivider(final Canvas canvas,final int left)
{
mDivider.setBounds(left,getPaddingTop()+mDividerPadding,left+mDividerWidth,getHeight()-getPaddingBottom()-mDividerPadding);
mDivider.draw(canvas);
}
/**
* Determines where to position dividers between children.
*
* #param childIndex Index of child to check for preceding divider
* #return true if there should be a divider before the child at childIndex
* #hide Pending API consideration. Currently only used internally by the system.
*/
protected boolean hasDividerBeforeChildAt(final int childIndex)
{
if(childIndex==0)
return (mShowDividers&SHOW_DIVIDER_BEGINNING)!=0;
else if(childIndex==getChildCount())
return (mShowDividers&SHOW_DIVIDER_END)!=0;
else if((mShowDividers&SHOW_DIVIDER_MIDDLE)!=0)
{
boolean hasVisibleViewBefore=false;
for(int i=childIndex-1;i>=0;i--)
if(getChildAt(i).getVisibility()!=GONE)
{
hasVisibleViewBefore=true;
break;
}
return hasVisibleViewBefore;
}
return false;
}
#Override
public int getDividerPadding()
{
return mDividerPadding;
}
#Override
public void setDividerPadding(final int dividerPadding)
{
mDividerPadding=dividerPadding;
}
#Override
public void setShowDividers(final int showDividers)
{
if(mShowDividers!=showDividers)
requestLayout();
mShowDividers=showDividers;
}
#Override
public void setDividerDrawable(final Drawable divider)
{
if(divider==mDivider)
return;
mDivider=divider;
if(divider!=null)
{
mDividerWidth=divider.getIntrinsicWidth();
mDividerHeight=divider.getIntrinsicHeight();
}
else
{
mDividerWidth=0;
mDividerHeight=0;
}
setWillNotDraw(divider==null);
requestLayout();
}
#Override
public Drawable getDividerDrawable()
{
return mDivider;
}
}
I'd like to have a view in my activity, which initially stays at the top of the screen like a little bar, but when you tap on it it should expand down, like the system notification area.
I haven't found any standard controls with such behaviour. What's the best way to implement this?
Use a SlidingDrawer. Here is a good tutorial.
The SlidingDrawer works exactly in this way.
The problem is that the SlidingDrawer can't be positioned at the top of the screen — it opens only upwards (see related question). So I implemented a simple control of my own, using the TranslateAnimation
class MySlidingDrawer extends LinearLayout {
public static final int STATE_OPENED = 0;
public static final int STATE_CLOSED = 1;
private int m_intState;
private LinearLayout m_content;
private ImageButton m_handle;
public MySlidingDrawer(Context context) {
super(context);
setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
setOrientation(VERTICAL);
setGravity(Gravity.CENTER);
m_content = new LinearLayout(context);
// add your content here
addView(m_content);
m_intState = STATE_CLOSED;
m_handle = new ImageButton(context);
m_handle.setImageResource(R.drawable.icon);
m_handle.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
toggleState();
}
});
m_handle.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
addView(m_handle);
}
private int getContentHeight() {
return m_content.getHeight();
}
private void toggleState() {
int intYStart = 0;
int intYEnd = m_intState == STATE_OPENED ? -getContentHeight() : getContentHeight();
Animation a = new TranslateAnimation(0.0f, 0.0f, intYStart, intYEnd);
a.setDuration(1000);
a.setStartOffset(300);
a.setInterpolator(AnimationUtils.loadInterpolator(getContext(), android.R.anim.bounce_interpolator));
startAnimation(a);
m_intState = m_intState == STATE_OPENED ? STATE_CLOSED : STATE_OPENED;
}
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
offsetTopAndBottom(-getContentHeight()); // content is initially invisible
}
protected void onAnimationEnd() {
super.onAnimationEnd();
int intYOffset = m_intState == STATE_OPENED ? getContentHeight() : -getContentHeight();
offsetTopAndBottom(intYOffset);
}
}
You can use the code posted in this answer: Android SlidingDrawer from top?
The provided solution features setting the orientation of the Slidingdrawer in xml also it's simple requiring only 1 class and some additions in attrs.xml and stable since it's derived from Androids Slidingdrawer from SDK. I also discuss why I didn't choose other popular libs/solutions found on the internet/SO.
Quicklink to the gist: MultipleOrientationSlidingDrawer (source & example) # gist