anim
slide_bottom_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="3000"
android:fromYDelta="0.0"
android:toYDelta="100.0%p" />
</set>
slide_top_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="3000"
android:fromYDelta="-100.0%p"
android:toYDelta="0.0" />
</set>
slide_left_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="3000"
android:fromXDelta="-100.0%p"
android:toXDelta="0.0" />
</set>
slide_left_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="3000"
android:fromXDelta="0.0"
android:toXDelta="-100.0%p" />
</set>
slide_right_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="3000"
android:fromXDelta="100.0%p"
android:toXDelta="0.0" />
</set>
slide_right_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="3000"
android:fromXDelta="0.0"
android:toXDelta="100.0%p" />
</set>
import java.util.ArrayList;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.ViewFlipper;
public class TileAnimator extends TileAnimatorLayout {
private static final String TAG = "TileAnimator";
private static final boolean LOGD = false;
private static final int DEFAULT_INTERVAL = 5000;
private int mFlipInterval = DEFAULT_INTERVAL;
private boolean mAutoStart = false;
private boolean mRunning = false;
private boolean mStarted = false;
private boolean mVisible = false;
private boolean mUserPresent = true;
private ArrayList<TilesFrameEntity> entities = new ArrayList<TilesFrameEntity>();
public TileAnimator(Context context) {
super(context);
}
public TileAnimator(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ArrayList<TilesFrameEntity> getEntities() {
return entities;
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equals(action)) {
mUserPresent = false;
updateRunning();
} else if (Intent.ACTION_USER_PRESENT.equals(action)) {
mUserPresent = true;
updateRunning(false);
}
}
};
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
getContext().registerReceiver(mReceiver, filter);
if (mAutoStart) {
startFlipping();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
getContext().unregisterReceiver(mReceiver);
updateRunning();
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == VISIBLE;
updateRunning(false);
}
/**
* How long to wait before flipping to the next view
*
* @param milliseconds
* time in milliseconds
*/
public void setFlipInterval(int milliseconds) {
mFlipInterval = milliseconds;
}
/**
* Start a timer to cycle through child views
*/
public void startFlipping() {
mStarted = true;
updateRunning();
}
/**
* No more flips
*/
public void stopFlipping() {
mStarted = false;
updateRunning();
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(ViewFlipper.class.getName());
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(ViewFlipper.class.getName());
}
/**
* Internal method to start or stop dispatching flip {@link Message} based
* on {@link #mRunning} and {@link #mVisible} state.
*/
private void updateRunning() {
updateRunning(true);
}
/**
* Internal method to start or stop dispatching flip {@link Message} based
* on {@link #mRunning} and {@link #mVisible} state.
*
* @param flipNow
* Determines whether or not to execute the animation now, in
* addition to queuing future flips. If omitted, defaults to
* true.
*/
private void updateRunning(boolean flipNow) {
boolean running = mVisible && mStarted && mUserPresent;
if (running != mRunning) {
if (running) {
showOnly(mWhichChild, flipNow);
Message msg = mHandler.obtainMessage(FLIP_MSG);
int interval = getInterval(mWhichChild);
mHandler.sendMessageDelayed(msg, interval);
} else {
mHandler.removeMessages(FLIP_MSG);
}
mRunning = running;
}
if (LOGD) {
Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted="
+ mStarted + ", mUserPresent=" + mUserPresent
+ ", mRunning=" + mRunning);
}
}
@Override
public void addView(View child) {
super.addView(child);
}
private int parse2Int(String s) {
int res = 5;
try {
res = Integer.parseInt(s);
if (res < 5) {
res = 5;
}
return res * 1000;
} catch (Exception e) {
Log.e(TAG, "parse2Int", e);
}
return mFlipInterval;
}
/**
* Returns true if the child views are flipping.
*/
public boolean isFlipping() {
return mStarted;
}
/**
* 添加瓷片元素
* @param entities
* @param type
*/
public void setTilesFrame(ArrayList<TilesFrameEntity> entities, int type) {
stopFlipping();
this.entities = entities;
this.removeAllViews();
for (TilesFrameEntity e : entities) {
ImageView img = new ImageView(getContext());
if (e.mDrawable != null) {
img.setBackgroundDrawable(e.mDrawable);
}
img.setScaleType(ScaleType.FIT_XY);
this.addView(img, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.FILL_PARENT,
FrameLayout.LayoutParams.FILL_PARENT));
}
mAutoStart = true;
startFlipping();
}
/**
* 获取间隔时间
* @param next
* @return
*/
private int getInterval(int next) {
if (next >= getChildCount()) {
next = 0;
} else if (next < 0) {
next = getChildCount() - 1;
}
if (entities != null && !entities.isEmpty() && next >= 0
&& next < entities.size()) {
return parse2Int(entities.get(next).getDuration());
}
return mFlipInterval;
}
/**
* Returns true if this view automatically calls {@link #startFlipping()}
* when it becomes attached to a window.
*/
public boolean isAutoStart() {
return mAutoStart;
}
private final int FLIP_MSG = 1;
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == FLIP_MSG) {
if (mRunning) {
showNext();
msg = obtainMessage(FLIP_MSG);
int interval = getInterval(mWhichChild + 1);
sendMessageDelayed(msg, interval);
}
}
}
};
}
TileAnimatorLayout.java
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ViewAnimator;
public class TileAnimatorLayout extends FrameLayout {
public TileAnimatorLayout(Context context) {
super(context);
initViewAnimator(context, null);
}
public TileAnimatorLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initViewAnimator(context, attrs);
}
int mWhichChild = 0;
boolean mFirstTime = true;
boolean mAnimateFirstTime = true;
Animation mInAnimation;
Animation mOutAnimation;
boolean mMeasureAllChildren = true;
/**
* Initialize this {@link ViewAnimator}, possibly setting
* {@link #setMeasureAllChildren(boolean)} based on {@link FrameLayout}
* flags.
*/
private void initViewAnimator(Context context, AttributeSet attrs) {
if (attrs == null) {
// For compatibility, always measure children when undefined.
mMeasureAllChildren = true;
return;
}
// TypedArray a = context.obtainStyledAttributes(attrs,
// R.styleable.tilesFrameAnimation);
// int resource = a.getResourceId(
// R.styleable.tilesFrameAnimation_inAnimation, 0);
// if (resource > 0) {
// setInAnimation(context, resource);
// }
//
// resource = a.getResourceId(
// R.styleable.tilesFrameAnimation_outAnimation, 0);
// if (resource > 0) {
// setOutAnimation(context, resource);
// }
setAnimateFirstView(true);
// a.recycle();
setMeasureAllChildren(true);
}
/**
* Sets which child view will be displayed.
*
* @param whichChild
* the index of the child view to display
*/
public void setDisplayedChild(int whichChild) {
mWhichChild = whichChild;
if (whichChild >= getChildCount()) {
mWhichChild = 0;
} else if (whichChild < 0) {
mWhichChild = getChildCount() - 1;
}
boolean hasFocus = getFocusedChild() != null;
// This will clear old focus if we had it
showOnly(mWhichChild);
if (hasFocus) {
// Try to retake focus if we had it
requestFocus(FOCUS_FORWARD);
}
}
/**
* Returns the index of the currently displayed child view.
*/
public int getDisplayedChild() {
return mWhichChild;
}
/**
* Manually shows the next child.
*/
public void showNext() {
setDisplayedChild(mWhichChild + 1);
}
/**
* Manually shows the previous child.
*/
public void showPrevious() {
setDisplayedChild(mWhichChild - 1);
}
/**
* Shows only the specified child. The other displays Views exit the screen,
* optionally with the with the {@link #getOutAnimation() out animation} and
* the specified child enters the screen, optionally with the
* {@link #getInAnimation() in animation}.
*
* @param childIndex
* The index of the child to be shown.
* @param animate
* Whether or not to use the in and out animations, defaults to
* true.
*/
void showOnly(int childIndex, boolean animate) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (i == childIndex) {
if (animate && mInAnimation != null && count > 1) {
child.startAnimation(mInAnimation);
}
child.setVisibility(View.VISIBLE);
mFirstTime = false;
} else {
if (animate && mOutAnimation != null
&& child.getVisibility() == View.VISIBLE) {
if (count > 1) {
child.startAnimation(mOutAnimation);
}
} else if (child.getAnimation() == mInAnimation)
child.clearAnimation();
child.setVisibility(View.GONE);
}
}
}
/**
* Shows only the specified child. The other displays Views exit the screen
* with the {@link #getOutAnimation() out animation} and the specified child
* enters the screen with the {@link #getInAnimation() in animation}.
*
* @param childIndex
* The index of the child to be shown.
*/
void showOnly(int childIndex) {
final boolean animate = (!mFirstTime || mAnimateFirstTime);
showOnly(childIndex, animate);
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
super.addView(child, index, params);
if (getChildCount() == 1) {
child.setVisibility(View.VISIBLE);
} else {
child.setVisibility(View.GONE);
}
if (index >= 0 && mWhichChild >= index) {
// Added item above current one, increment the index of the
// displayed child
setDisplayedChild(mWhichChild + 1);
}
}
@Override
public void removeAllViews() {
super.removeAllViews();
mWhichChild = 0;
mFirstTime = true;
}
@Override
public void removeView(View view) {
final int index = indexOfChild(view);
if (index >= 0) {
removeViewAt(index);
}
}
@Override
public void removeViewAt(int index) {
super.removeViewAt(index);
final int childCount = getChildCount();
if (childCount == 0) {
mWhichChild = 0;
mFirstTime = true;
} else if (mWhichChild >= childCount) {
// Displayed is above child count, so float down to top of stack
setDisplayedChild(childCount - 1);
} else if (mWhichChild == index) {
// Displayed was removed, so show the new child living in its place
setDisplayedChild(mWhichChild);
}
}
public void removeViewInLayout(View view) {
removeView(view);
}
public void removeViews(int start, int count) {
super.removeViews(start, count);
if (getChildCount() == 0) {
mWhichChild = 0;
mFirstTime = true;
} else if (mWhichChild >= start && mWhichChild < start + count) {
// Try showing new displayed child, wrapping if needed
setDisplayedChild(mWhichChild);
}
}
public void removeViewsInLayout(int start, int count) {
removeViews(start, count);
}
/**
* Returns the View corresponding to the currently displayed child.
*
* @return The View currently displayed.
*
* @see #getDisplayedChild()
*/
public View getCurrentView() {
return getChildAt(mWhichChild);
}
/**
* Returns the current animation used to animate a View that enters the
* screen.
*
* @return An Animation or null if none is set.
*
* @see #setInAnimation(android.view.animation.Animation)
* @see #setInAnimation(android.content.Context, int)
*/
public Animation getInAnimation() {
return mInAnimation;
}
/**
* Specifies the animation used to animate a View that enters the screen.
*
* @param inAnimation
* The animation started when a View enters the screen.
*
* @see #getInAnimation()
* @see #setInAnimation(android.content.Context, int)
*/
public void setInAnimation(Animation inAnimation) {
mInAnimation = inAnimation;
}
/**
* Returns the current animation used to animate a View that exits the
* screen.
*
* @return An Animation or null if none is set.
*
* @see #setOutAnimation(android.view.animation.Animation)
* @see #setOutAnimation(android.content.Context, int)
*/
public Animation getOutAnimation() {
return mOutAnimation;
}
/**
* Specifies the animation used to animate a View that exit the screen.
*
* @param outAnimation
* The animation started when a View exit the screen.
*
* @see #getOutAnimation()
* @see #setOutAnimation(android.content.Context, int)
*/
public void setOutAnimation(Animation outAnimation) {
mOutAnimation = outAnimation;
}
/**
* Specifies the animation used to animate a View that enters the screen.
*
* @param context
* The application's environment.
* @param resourceID
* The resource id of the animation.
*
* @see #getInAnimation()
* @see #setInAnimation(android.view.animation.Animation)
*/
public void setInAnimation(Context context, int resourceID) {
setInAnimation(AnimationUtils.loadAnimation(context, resourceID));
}
/**
* Specifies the animation used to animate a View that exit the screen.
*
* @param context
* The application's environment.
* @param resourceID
* The resource id of the animation.
*
* @see #getOutAnimation()
* @see #setOutAnimation(android.view.animation.Animation)
*/
public void setOutAnimation(Context context, int resourceID) {
setOutAnimation(AnimationUtils.loadAnimation(context, resourceID));
}
/**
* Returns whether the current View should be animated the first time the
* ViewAnimator is displayed.
*
* @return true if the current View will be animated the first time it is
* displayed, false otherwise.
*
* @see #setAnimateFirstView(boolean)
*/
public boolean getAnimateFirstView() {
return mAnimateFirstTime;
}
/**
* Indicates whether the current View should be animated the first time the
* ViewAnimator is displayed.
*
* @param animate
* True to animate the current View the first time it is
* displayed, false otherwise.
*/
public void setAnimateFirstView(boolean animate) {
mAnimateFirstTime = animate;
}
@Override
public int getBaseline() {
return (getCurrentView() != null) ? getCurrentView().getBaseline()
: super.getBaseline();
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(ViewAnimator.class.getName());
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(ViewAnimator.class.getName());
}
}
备注:TileAnimator是ViewFlipper源码,只是增加了添加瓷片的类以及获取瓷片停留时间的getInterval(int next)方法。
TileAnimatorLayout是ViewAnimator的源码,唯一的不同就是增加了个数为一个时,不进行动画的判断,另外可以使TileAnimator调用showOnly(mWhichChild, flipNow)方法。