Improve Scene

This commit is contained in:
seven332 committed 2015-08-01 19:03:54 +08:00
1 parent c47b8a021e
commit 41ed55188b
22 files changed
+746 -584

No files matched your search

@@ -81,6 +81,12 @@ public class EhApplication extends SceneApplication {
// TODO Can't create spider info dir
}
GallerySpider.init(mEhHttpClient, spiderInfoDir);
Settings.getImageDownloadLocation().ensureDir();
Settings.getArchiveDownloadLocation().ensureDir();
Settings.getImageDownloadLocation().listFiles();
}
private int getMemoryCacheMaxSize() {
@@ -22,8 +22,7 @@ import android.util.Log;
import com.hippo.ehviewer.gallery.gifdecoder.GifDecoder;
import java.util.concurrent.locks.ReentrantLock;
// TODO read file to memory or do jni
public class GifTexture extends TiledTexture {
private static final String TAG = GifTexture.class.getSimpleName();
@@ -33,7 +32,7 @@ public class GifTexture extends TiledTexture {
private GifDecoder mGifDecoder;
private boolean mRunning = false;
private final ReentrantLock mLock = new ReentrantLock();
private volatile boolean mDecoding = false;
private GifDecodeTask mGifDecodeTask;
@@ -61,7 +60,7 @@ public class GifTexture extends TiledTexture {
GifDecoder gifDecoder = mGifDecoder;
while (mRunning && sUploader != null) {
mLock.lock();
mDecoding = true;
gifDecoder.advance();
Bitmap bitmap = gifDecoder.getNextFrame();
@@ -83,9 +82,14 @@ public class GifTexture extends TiledTexture {
if (mLastTime != 0) {
delay -= time - mLastTime;
}
//Say.d(TAG, "time - mLastTime = " + (time - mLastTime));
//Say.d(TAG, "gifDecoder.getNextDelay() = " + (gifDecoder.getNextDelay()));
mLastTime = time;
mLock.unlock();
mDecoding = false;
if (delay > 5) {
try {
@@ -124,7 +128,7 @@ public class GifTexture extends TiledTexture {
mRunning = false;
mGifDecodeTask = null;
if (!mLock.isLocked()) {
if (!mDecoding) {
mGifDecoder.resetFrameIndex();
mGifDecoder.advance();
Bitmap bitmap = mGifDecoder.getNextFrame();
@@ -142,7 +146,7 @@ public class GifTexture extends TiledTexture {
public void recycle() {
mRunning = false;
if (mLock.isLocked()) {
if (mDecoding) {
mGifDecodeTask.clearByMyself();
} else {
mGifDecoder.clear();
@@ -130,7 +130,9 @@ public class GLRootView extends GLSurfaceView
// for example CyanogenMod use ro.opengles.surface.rgb565,
// so just use default EGLConfigChooser.
// And we do not need depth buffer
setEGLConfigChooser(false);
// TODO setEGLConfigChooser(8, 8, 8, 8, 0, 0) or setEGLConfigChooser(false)
setEGLConfigChooser(8, 8, 8, 8, 0, 0);
//setEGLConfigChooser(false);
setRenderer(this);
// setFormat is done by SurfaceView in SDK 2.3 and newer.
//if (ApiHelper.USE_888_PIXEL_FORMAT) {
@@ -36,7 +36,6 @@ import com.hippo.ehviewer.widget.DrawerLeftPanel;
import com.hippo.ehviewer.widget.FitStageLayout;
import com.hippo.scene.Announcer;
import com.hippo.scene.SceneManager;
import com.hippo.scene.SimpleCurtain;
import com.hippo.scene.StageActivity;
import com.hippo.scene.StageLayout;
import com.hippo.vectorold.content.VectorContext;
@@ -87,7 +86,7 @@ public final class ContentActivity extends StageActivity
break;
case DRAWER_LIST_SETTINGS:
mDrawerLayout.closeDrawers();
startScene(MainSettingsScene.class, null, new SimpleCurtain(SimpleCurtain.DIRECTION_BOTTOM));
startScene(MainSettingsScene.class);
break;
}
@@ -174,41 +173,6 @@ public final class ContentActivity extends StageActivity
if (savedInstanceState == null) {
startScene(GalleryListScene.class, null);
}
/*
try {
BitmapPool bitmapPool = new BitmapPool();
ByteArrayPool byteArrayPool = new ByteArrayPool();
GifBitmapProvider gifBitmapProvider = new GifBitmapProvider(bitmapPool, byteArrayPool);
File file = new File(Environment.getExternalStorageDirectory(), "00000001.gif");
UniRandomReadFile randomReadFile = UniRandomReadFile.fromFile(file);
GifHeader gifHeader = new GifHeaderParser().setData(randomReadFile).parseHeader();
if (gifHeader.getStatus() == GifDecoder.STATUS_OK) {
GifDecoder gifDecoder = new GifDecoder(gifBitmapProvider, gifHeader, randomReadFile);
for (int i = 0, n = gifDecoder.getFrameCount(); i < n * 2; i++) {
gifDecoder.advance();
gifDecoder.getNextFrame().compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(new File(Environment.getExternalStorageDirectory(), String.format("%08d", i) + ".png")));
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
*/
}
@Override
@@ -267,7 +231,7 @@ public final class ContentActivity extends StageActivity
@Override
public void onClickSignIn() {
mDrawerLayout.closeDrawers();
startScene(SignInScene.class, null, new SimpleCurtain(SimpleCurtain.DIRECTION_BOTTOM));
startScene(SignInScene.class);
}
@Override
@@ -56,9 +56,11 @@ import com.hippo.ehviewer.widget.RatingView;
import com.hippo.miscellaneous.StartActivityHelper;
import com.hippo.rippleold.RippleSalon;
import com.hippo.scene.Announcer;
import com.hippo.scene.OffsetCurtain;
import com.hippo.scene.RippleCurtain;
import com.hippo.scene.Scene;
import com.hippo.scene.SimpleDialog;
import com.hippo.scene.UnionCurtain;
import com.hippo.util.URLImageGetter;
import com.hippo.widget.AccurateClick;
import com.hippo.widget.AutoWrapLayout;
@@ -136,6 +138,48 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener,
client.execute(request);
}
@Override
protected void onCreate(boolean rebirth) {
super.onCreate(rebirth);
setContentView(R.layout.scene_gallery_detail);
mDetailFrameLayout = (FrameLayout) findViewById(R.id.detail_frame_layout);
mHeader = (ViewGroup) findViewById(R.id.header);
mThumb = (LoadImageView) mHeader.findViewById(R.id.thumb);
mTitle = (TextView) mHeader.findViewById(R.id.title);
mUploader = (TextView) mHeader.findViewById(R.id.uploader);
mCategory = (TextView) mHeader.findViewById(R.id.category);
mActionCard = (ViewGroup) findViewById(R.id.action_card);
mRead = mActionCard.findViewById(R.id.read);
mDownload = mActionCard.findViewById(R.id.download);
mContent = (ViewGroup) findViewById(R.id.content);
mInfo = (ViewGroup) mContent.findViewById(R.id.info);
mLanguage = (TextView) mInfo.findViewById(R.id.language);
mPages = (TextView) mInfo.findViewById(R.id.pages);
mSize = (TextView) mInfo.findViewById(R.id.size);
mPosted = (TextView) mInfo.findViewById(R.id.posted);
mResize = (TextView) mInfo.findViewById(R.id.resize);
mFavoredTimes = (TextView) mInfo.findViewById(R.id.favoredTimes);
mRatingText = (TextSwitcher) mContent.findViewById(R.id.rating_text);
mRating = (RatingView) mContent.findViewById(R.id.rating);
mFavorite = (TextView) mContent.findViewById(R.id.favorite);
mTorrent = (TextView) mContent.findViewById(R.id.torrent);
mShare = (TextView) mContent.findViewById(R.id.share);
mRate = (TextView) mContent.findViewById(R.id.rate);
mTag = (LinearLayout) mContent.findViewById(R.id.tag);
mNoTags = (TextView) mTag.findViewById(no_tags);
mComment = (LinearLayout) mContent.findViewById(R.id.comment);
mCommentMore = (TextView) mContent.findViewById(R.id.comment_more);
mPreviewLayout = (PreviewLayout) mContent.findViewById(R.id.preview);
mProgressBar = findViewById(R.id.progress_bar);
mViewTransition = new ViewTransition(mContent, mProgressBar);
}
private void handleAnnouncer(Announcer announcer) {
if (announcer == null) {
Say.e(TAG, "No announcer in GalleryDetailScene, finish itself");
@@ -211,55 +255,23 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener,
} else if (ACTION_URL.equals(action)) {
} else {
Say.e(TAG, "Unkonwn action " + action);
Say.e(TAG, "GalleryDetailScene: Unkonwn action " + action);
finish();
}
}
@Override
protected void onCreate(boolean rebirth) {
super.onCreate(rebirth);
setContentView(R.layout.scene_gallery_detail);
mDetailFrameLayout = (FrameLayout) findViewById(R.id.detail_frame_layout);
mHeader = (ViewGroup) findViewById(R.id.header);
mThumb = (LoadImageView) mHeader.findViewById(R.id.thumb);
mTitle = (TextView) mHeader.findViewById(R.id.title);
mUploader = (TextView) mHeader.findViewById(R.id.uploader);
mCategory = (TextView) mHeader.findViewById(R.id.category);
mActionCard = (ViewGroup) findViewById(R.id.action_card);
mRead = mActionCard.findViewById(R.id.read);
mDownload = mActionCard.findViewById(R.id.download);
mContent = (ViewGroup) findViewById(R.id.content);
mInfo = (ViewGroup) mContent.findViewById(R.id.info);
mLanguage = (TextView) mInfo.findViewById(R.id.language);
mPages = (TextView) mInfo.findViewById(R.id.pages);
mSize = (TextView) mInfo.findViewById(R.id.size);
mPosted = (TextView) mInfo.findViewById(R.id.posted);
mResize = (TextView) mInfo.findViewById(R.id.resize);
mFavoredTimes = (TextView) mInfo.findViewById(R.id.favoredTimes);
mRatingText = (TextSwitcher) mContent.findViewById(R.id.rating_text);
mRating = (RatingView) mContent.findViewById(R.id.rating);
mFavorite = (TextView) mContent.findViewById(R.id.favorite);
mTorrent = (TextView) mContent.findViewById(R.id.torrent);
mShare = (TextView) mContent.findViewById(R.id.share);
mRate = (TextView) mContent.findViewById(R.id.rate);
mTag = (LinearLayout) mContent.findViewById(R.id.tag);
mNoTags = (TextView) mTag.findViewById(no_tags);
mComment = (LinearLayout) mContent.findViewById(R.id.comment);
mCommentMore = (TextView) mContent.findViewById(R.id.comment_more);
mPreviewLayout = (PreviewLayout) mContent.findViewById(R.id.preview);
mProgressBar = findViewById(R.id.progress_bar);
mViewTransition = new ViewTransition(mContent, mProgressBar);
protected void onBind() {
super.onBind();
handleAnnouncer(getAnnouncer());
}
@Override
protected void onRestore() {
super.onRestore();
}
@Override
protected void onGetFitPaddingBottom(int b) {
mDetailFrameLayout.setPadding(0, 0, 0, b);
@@ -305,14 +317,16 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener,
Announcer announcer = new Announcer();
announcer.putExtra(InfoScene.KEY_INFO, mGalleryDetail);
startScene(InfoScene.class, announcer,
new RippleCurtain((int) x + position[0], (int) y + position[1]));
new UnionCurtain(new RippleCurtain((int) x + position[0], (int) y + position[1]),
new OffsetCurtain(OffsetCurtain.DIRECTION_BOTTOM)));
} else if (v == mComment) {
int[] position = new int[2];
ViewUtils.getLocationInAncestor(mComment, position, getSceneView());
Announcer announcer = new Announcer();
announcer.putExtra(CommentScene.KEY_COMMENTS, mGalleryDetail.comments);
startScene(CommentScene.class, announcer,
new RippleCurtain((int) x + position[0], (int) y + position[1]));
new UnionCurtain(new RippleCurtain((int) x + position[0], (int) y + position[1]),
new OffsetCurtain(OffsetCurtain.DIRECTION_BOTTOM)));
}
}
@@ -66,7 +66,7 @@ import com.hippo.scene.Announcer;
import com.hippo.scene.Curtain;
import com.hippo.scene.Scene;
import com.hippo.scene.SimpleDialog;
import com.hippo.scene.TransitionCurtain;
import com.hippo.scene.ElementsSharedCurtain;
import com.hippo.util.AnimationUtils;
import com.hippo.widget.FabLayout;
import com.hippo.widget.FloatingActionButton;
@@ -752,7 +752,7 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
announcer.setAction(GalleryDetailScene.ACTION_GALLERY_INFO);
announcer.putExtra(GalleryDetailScene.KEY_GALLERY_INFO, gi);
Curtain curtain = new TransitionCurtain(new TransitonHelper(position));
Curtain curtain = new ElementsSharedCurtain(new TransitonHelper(position));
startScene(GalleryDetailScene.class, announcer, curtain);
@@ -766,7 +766,7 @@ public final class GalleryListScene extends Scene implements SearchBar.Helper,
}
}
private class TransitonHelper implements TransitionCurtain.ViewPairSet {
private class TransitonHelper implements ElementsSharedCurtain.ViewPairSet {
private int mPosition;
@@ -34,8 +34,8 @@ public class DownloadSettingsScene extends PreferenceScene implements Preference
private static final String KEY_IMAGE_DOWNLOAD_LOACTION = "image_download_location";
private static final String KEY_ARCHIVE_DOWNLOAD_LOACTION = "archive_download_location";
private static Preference mImagePreference;
private static Preference mArchivePreference;
private Preference mImagePreference;
private Preference mArchivePreference;
@Override
protected void onCreate(boolean rebirth) {
@@ -23,7 +23,6 @@ import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.EhConfig;
import com.hippo.ehviewer.util.Settings;
import com.hippo.scene.SimpleCurtain;
import com.hippo.scene.preference.ListPreference;
import com.hippo.scene.preference.Preference;
import com.hippo.scene.preference.PreferenceCategory;
@@ -103,7 +102,7 @@ public final class EHSettingsScene extends PreferenceScene implements Preference
public boolean onClick(Preference preference) {
String key = preference.getKey();
if (Settings.KEY_EXCLUDED_LANGUAGES.equals(key)) {
startScene(ExcludedLanguagesScene.class, null, new SimpleCurtain(SimpleCurtain.DIRECTION_BOTTOM));
startScene(ExcludedLanguagesScene.class);
return true;
} else {
return false;
@@ -242,7 +242,7 @@ public final class Settings {
} catch (Exception e) {
e.printStackTrace();
}
return dir != null ? dir : UniFile.fromFile(AppConfig.getDownloadDir());
return dir != null ? dir : UniFile.fromFile(AppConfig.getArchiveDir());
}
public static void putArchiveDownloadLocation(UniFile location) {
+6 -11
View File
@@ -23,7 +23,7 @@ public abstract class Curtain {
private int mPreviousSceneId;
void setPreviousScene(@Nullable Scene pScene) {
protected void setPreviousScene(@Nullable Scene pScene) {
if (pScene == null) {
mPreviousSceneId = -1;
} else {
@@ -31,17 +31,20 @@ public abstract class Curtain {
}
}
boolean isPreviousScene(@Nullable Scene scene) {
protected boolean isPreviousScene(@Nullable Scene scene) {
return scene != null && scene.getId() == mPreviousSceneId;
}
/**
* @return true if can NOT still run when previous scene changed
*/
protected boolean specifyPreviousScene() {
protected boolean needSpecifyPreviousScene() {
return true;
}
/**
* Scene rebirth
*/
protected void onRebirth() {
}
@@ -72,12 +75,4 @@ public abstract class Curtain {
protected void dispatchCloseFinished(@NonNull Scene enter, @NonNull Scene exit) {
exit.closeFinished();
}
protected void hideSceneOnOpen(@NonNull Scene exit) {
exit.setHide(true);
}
protected void showSceneOnClose(@NonNull Scene enter) {
enter.setHide(false);
}
}
@@ -31,16 +31,15 @@ import com.hippo.animation.ArgbEvaluator;
import com.hippo.animation.SimpleAnimatorListener;
import com.hippo.util.AnimationUtils;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.widget.GlobalLayoutSet;
import com.hippo.yorozuya.Say;
import com.hippo.yorozuya.ViewUtils;
import java.util.HashSet;
import java.util.Set;
public class TransitionCurtain extends Curtain {
public class ElementsSharedCurtain extends Curtain {
private static final String TAG = TransitionCurtain.class.getSimpleName();
private static final String TAG = ElementsSharedCurtain.class.getSimpleName();
private static long ANIMATE_TIME = 300L;
@@ -48,16 +47,27 @@ public class TransitionCurtain extends Curtain {
private AnimatorSet mAnimatorSet;
public TransitionCurtain(ViewPairSet viewPairSet) {
public ElementsSharedCurtain(ViewPairSet viewPairSet) {
mViewPairSet = viewPairSet;
}
// TODO what if back key press before move animator start
// Make background from transport to default.
// Invisible other
@Override
public void open(final @NonNull Scene enter, final @NonNull Scene exit) {
// Check enter content view
View sceneContentView = enter.getSceneView().getChildAt(0);
if (sceneContentView == null) {
Say.w(TAG, "Can't get enter scene content view");
dispatchOpenFinished(enter, exit);
return;
}
// Check stage layout isLayoutRequested
final StageLayout stageLayout = enter.getStageLayout();
if (!stageLayout.isLayoutRequested()) {
Say.w(TAG, "WTF? stageLayout.isLayoutRequested() == false");
dispatchOpenFinished(enter, exit);
return;
}
final Set<Animator> animatorCollection = new HashSet<>();
@@ -65,23 +75,17 @@ public class TransitionCurtain extends Curtain {
int bgColor = enter.getBackgroundColor();
int startBgColor = bgColor & 0xffffff;
enter.setBackgroundColor(startBgColor);
ObjectAnimator colorAnim = ObjectAnimator.ofInt(enter, "backgroundColor", bgColor);
ObjectAnimator colorAnim = ObjectAnimator.ofInt(enter, "backgroundColor", startBgColor, bgColor);
colorAnim.setEvaluator(ArgbEvaluator.getInstance());
colorAnim.setDuration(ANIMATE_TIME);
animatorCollection.add(colorAnim);
View sceneContentView = enter.getSceneView().getChildAt(0);
if (sceneContentView != null) {
sceneContentView.setAlpha(0f);
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(sceneContentView, "alpha", 0f, 1f);
alphaAnim.setDuration(ANIMATE_TIME);
animatorCollection.add(alphaAnim);
} else {
Say.w(TAG, "Can't get enter scene content view");
}
sceneContentView.setAlpha(0f);
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(sceneContentView, "alpha", 0f, 1f);
alphaAnim.setDuration(ANIMATE_TIME);
animatorCollection.add(alphaAnim);
GlobalLayoutSet globalLayoutSet = new GlobalLayoutSet();
Set<TransitionItem> transitionItemSet = new HashSet<>();
final Set<TransitionItem> transitionItemSet = new HashSet<>();
// Handle transit part
View[] enterViews = mViewPairSet.getToViewSet(enter);
@@ -108,131 +112,119 @@ public class TransitionCurtain extends Curtain {
item.enterView = enterView;
item.exitView = exitView;
transitionItemSet.add(item);
globalLayoutSet.addListenerToObserver(enterView.getViewTreeObserver());
}
if (globalLayoutSet.getSize() == 0) {
// Not transition view, do animation now
new OpenSecondAllLayoutListener(enter, exit, transitionItemSet, animatorCollection).onAllLayout();
if (transitionItemSet.isEmpty()) {
stageLayout.addOnLayoutListener(new OpenSecondLayoutListener(enter, exit, transitionItemSet, animatorCollection));
} else {
globalLayoutSet.setOnAllLayoutListener(new OpenFirstAllLayoutListener(
enter, exit, transitionItemSet, animatorCollection));
stageLayout.addOnLayoutListener(new OpenFirstLayoutListener(enter, exit, transitionItemSet, animatorCollection));
}
}
private class OpenFirstAllLayoutListener implements GlobalLayoutSet.OnAllLayoutListener{
private class OpenFirstLayoutListener implements StageLayout.OnLayoutListener {
Scene mEnterScene;
Scene mExitScene;
Set<TransitionItem> mTransitionItemSet;
Set<Animator> mAnimatorCollection;
private Scene mEnter;
private Scene mExit;
private SceneView mEnterView;
private SceneView mExitView;
private StageLayout mStageLayout;
private Set<TransitionItem> mTransitionItems;
private Set<Animator> mAnimatorCollection;
public OpenFirstAllLayoutListener(Scene enterScene, Scene exitScene,
Set<TransitionItem> transitionItemSet, Set<Animator> animatorCollection) {
mEnterScene = enterScene;
mExitScene = exitScene;
mTransitionItemSet = transitionItemSet;
public OpenFirstLayoutListener(Scene enter, Scene exit, Set<TransitionItem> transitionItems,
Set<Animator> animatorCollection) {
mEnter = enter;
mExit = exit;
mEnterView = enter.getSceneView();
mExitView = exit.getSceneView();
mStageLayout = enter.getStageLayout();
mTransitionItems = transitionItems;
mAnimatorCollection = animatorCollection;
}
@SuppressWarnings("deprecation")
@Override
public void onAllLayout() {
Set<TransitionItem> transitionItemSet = mTransitionItemSet;
public void onLayout(View view) {
mStageLayout.removeOnLayoutListener(this);
ViewGroup enterSceneView = mEnterScene.getSceneView();
GlobalLayoutSet globalLayoutSet = new GlobalLayoutSet();
for (TransitionItem item : transitionItemSet) {
int[] startloaction = new int[2];
int[] endloaction = new int[2];
for (TransitionItem item : mTransitionItems) {
final View enterView = item.enterView;
final View exitView = item.exitView;
int[] startloaction = new int[2];
ViewUtils.getLocationInAncestor(exitView, startloaction, mExitScene.getSceneView());
int[] endloaction = new int[2];
ViewUtils.getLocationInAncestor(enterView, endloaction, mEnterScene.getSceneView());
ViewUtils.getLocationInAncestor(exitView, startloaction, mExitView);
ViewUtils.getLocationInAncestor(enterView, endloaction, mEnterView);
int startWidth = exitView.getWidth();
int startHeight = exitView.getHeight();
item.endWidth = enterView.getWidth();
item.endHeight = enterView.getHeight();
item.startWidth = startWidth;
item.startHeight = startHeight;
item.endloaction = endloaction;
// Create bitmap view
Bitmap bmp = ViewUtils.getBitmapFromView(enterView);
View bmpView = new View(enterView.getContext());
item.bmp = bmp;
item.bmpView = bmpView;
bmpView.setBackgroundDrawable(new BitmapDrawable(bmp));
bmpView.setBackgroundDrawable(new BitmapDrawable(enterView.getContext().getResources(), bmp));
AbsoluteLayout.LayoutParams lp = new AbsoluteLayout.LayoutParams(
startWidth, startHeight, startloaction[0], startloaction[1]);
enterSceneView.addView(bmpView, lp);
mEnterView.addView(bmpView, lp);
bmpView.setVisibility(View.INVISIBLE);
globalLayoutSet.addListenerToObserver(bmpView.getViewTreeObserver());
bmpView.setPivotX(0);
bmpView.setPivotY(0);
PropertyValuesHolder scaleXPvh = PropertyValuesHolder.ofFloat("scaleX", 1f, (float) enterView.getWidth() / startWidth);
PropertyValuesHolder scaleYPvh = PropertyValuesHolder.ofFloat("scaleY", 1f, (float) enterView.getHeight() / startHeight);
PropertyValuesHolder xPvh = PropertyValuesHolder.ofFloat("x", startloaction[0], endloaction[0]);
PropertyValuesHolder yPvh = PropertyValuesHolder.ofFloat("y", startloaction[1], endloaction[1]);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(bmpView, scaleXPvh, scaleYPvh, xPvh, yPvh);
anim.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
anim.setDuration(ANIMATE_TIME);
mAnimatorCollection.add(anim);
}
globalLayoutSet.setOnAllLayoutListener(new OpenSecondAllLayoutListener(
mEnterScene, mExitScene, transitionItemSet, mAnimatorCollection));
mStageLayout.addOnLayoutListener(new OpenSecondLayoutListener(mEnter, mExit, mTransitionItems, mAnimatorCollection));
}
}
private class OpenSecondAllLayoutListener implements GlobalLayoutSet.OnAllLayoutListener{
private class OpenSecondLayoutListener implements StageLayout.OnLayoutListener {
Scene mEnterScene;
Scene mExitScene;
Set<TransitionItem> mTransitionItemSet;
Set<Animator> mAnimatorCollection;
private Scene mEnter;
private Scene mExit;
private Set<TransitionItem> mTransitionItems;
private Set<Animator> mAnimatorCollection;
public OpenSecondAllLayoutListener(Scene enterScene, Scene exitScene,
Set<TransitionItem> transitionItemSet, Set<Animator> animatorCollection) {
mEnterScene = enterScene;
mExitScene = exitScene;
mTransitionItemSet = transitionItemSet;
public OpenSecondLayoutListener(Scene enter, Scene exit, Set<TransitionItem> transitionItems,
Set<Animator> animatorCollection) {
mEnter = enter;
mExit = exit;
mTransitionItems = transitionItems;
mAnimatorCollection = animatorCollection;
}
@Override
public void onAllLayout() {
Set<TransitionItem> transitionItemSet = mTransitionItemSet;
Set<Animator> animatorCollection = mAnimatorCollection;
public void onLayout(View view) {
mEnter.getStageLayout().removeOnLayoutListener(this);
for (TransitionItem item : transitionItemSet) {
for (TransitionItem item : mTransitionItems) {
View exitView = item.exitView;
View bmpView = item.bmpView;
exitView.setVisibility(View.INVISIBLE);
bmpView.setVisibility(View.VISIBLE);
bmpView.setPivotX(0);
bmpView.setPivotY(0);
PropertyValuesHolder scaleXPvh = PropertyValuesHolder.ofFloat("scaleX", (float) item.endWidth / item.startWidth);
PropertyValuesHolder scaleYPvh = PropertyValuesHolder.ofFloat("scaleY", (float) item.endHeight/ item.startHeight);
PropertyValuesHolder xPvh = PropertyValuesHolder.ofFloat("x", item.endloaction[0]);
PropertyValuesHolder yPvh = PropertyValuesHolder.ofFloat("y", item.endloaction[1]);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(bmpView, scaleXPvh, scaleYPvh, xPvh, yPvh);
anim.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
anim.setDuration(ANIMATE_TIME);
animatorCollection.add(anim);
}
mAnimatorSet = new AnimatorSet();
mAnimatorSet.playTogether(animatorCollection);
mAnimatorSet.playTogether(mAnimatorCollection);
mAnimatorSet.addListener(new SimpleAnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
dispatchOpenFinished(mEnterScene, mExitScene);
hideSceneOnOpen(mExitScene);
for (TransitionItem item : mTransitionItemSet) {
dispatchOpenFinished(mEnter, mExit);
ViewGroup enterSceneView = mEnter.getSceneView();
for (TransitionItem item : mTransitionItems) {
item.enterView.setVisibility(View.VISIBLE);
item.exitView.setVisibility(View.VISIBLE);
ViewGroup enterSceneView = mEnterScene.getSceneView();
enterSceneView.removeView(item.bmpView);
item.bmp.recycle();
@@ -244,31 +236,30 @@ public class TransitionCurtain extends Curtain {
}
}
@Override
public void close(final @NonNull Scene enter, final @NonNull Scene exit) {
showSceneOnClose(enter);
// Check enter content view
View sceneContentView = exit.getSceneView().getChildAt(0);
if (sceneContentView == null) {
Say.w(TAG, "Can't get exit scene content view");
dispatchOpenFinished(enter, exit);
return;
}
final Set<Animator> animatorCollection = new HashSet<>();
// Handle background
int bgColor = exit.getBackgroundColor();
int endBgColor = bgColor & 0xffffff;
ObjectAnimator colorAnim = ObjectAnimator.ofInt(exit, "backgroundColor", endBgColor);
ObjectAnimator colorAnim = ObjectAnimator.ofInt(exit, "backgroundColor", bgColor, endBgColor);
colorAnim.setEvaluator(ArgbEvaluator.getInstance());
colorAnim.setDuration(ANIMATE_TIME);
animatorCollection.add(colorAnim);
View sceneContentView = exit.getSceneView().getChildAt(0);
if (sceneContentView != null) {
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(sceneContentView, "alpha", 0f);
alphaAnim.setDuration(ANIMATE_TIME);
animatorCollection.add(alphaAnim);
} else {
Say.w(TAG, "Can't get enter scene content view");
}
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(sceneContentView, "alpha", 1f, 0f);
alphaAnim.setDuration(ANIMATE_TIME);
animatorCollection.add(alphaAnim);
GlobalLayoutSet globalLayoutSet = new GlobalLayoutSet();
Set<TransitionItem> transitionItemSet = new HashSet<>();
// Handle transit part
@@ -296,56 +287,59 @@ public class TransitionCurtain extends Curtain {
item.enterView = enterView;
item.exitView = exitView;
transitionItemSet.add(item);
globalLayoutSet.addListenerToObserver(enterView.getViewTreeObserver());
}
if (globalLayoutSet.getSize() == 0) {
new CloseSecondAllLayoutListener(enter, exit, transitionItemSet, animatorCollection).onAllLayout();
final StageLayout stageLayout = enter.getStageLayout();
if (stageLayout.isLayoutRequested()) {
if (transitionItemSet.isEmpty()) {
stageLayout.addOnLayoutListener(new CloseSecondLayoutListener(enter, exit, transitionItemSet, animatorCollection));
} else {
stageLayout.addOnLayoutListener(new CloseFirstLayoutListener(enter, exit, transitionItemSet, animatorCollection));
}
} else {
globalLayoutSet.setOnAllLayoutListener(new CloseFirstAllLayoutListener(
enter, exit, transitionItemSet, animatorCollection));
if (transitionItemSet.isEmpty()) {
new CloseSecondLayoutListener(enter, exit, transitionItemSet, animatorCollection).onLayout(stageLayout);
} else {
new CloseFirstLayoutListener(enter, exit, transitionItemSet, animatorCollection).onLayout(stageLayout);
}
}
}
private class CloseFirstAllLayoutListener implements GlobalLayoutSet.OnAllLayoutListener{
private class CloseFirstLayoutListener implements StageLayout.OnLayoutListener {
Scene mEnterScene;
Scene mExitScene;
Set<TransitionItem> mTransitionItemSet;
Set<Animator> mAnimatorCollection;
private Scene mEnter;
private Scene mExit;
private SceneView mEnterView;
private SceneView mExitView;
private StageLayout mStageLayout;
private Set<TransitionItem> mTransitionItems;
private Set<Animator> mAnimatorCollection;
public CloseFirstAllLayoutListener(Scene enterScene, Scene exitScene,
Set<TransitionItem> transitionItemSet, Set<Animator> animatorCollection) {
mEnterScene = enterScene;
mExitScene = exitScene;
mTransitionItemSet = transitionItemSet;
public CloseFirstLayoutListener(Scene enter, Scene exit, Set<TransitionItem> transitionItems,
Set<Animator> animatorCollection) {
mEnter = enter;
mExit = exit;
mEnterView = enter.getSceneView();
mExitView = exit.getSceneView();
mStageLayout = enter.getStageLayout();
mTransitionItems = transitionItems;
mAnimatorCollection = animatorCollection;
}
@SuppressWarnings("deprecation")
@Override
public void onAllLayout() {
Set<TransitionItem> transitionItemSet = mTransitionItemSet;
public void onLayout(View view) {
mStageLayout.removeOnLayoutListener(this);
ViewGroup exitSceneView = mExitScene.getSceneView();
GlobalLayoutSet globalLayoutSet = new GlobalLayoutSet();
for (TransitionItem item : transitionItemSet) {
int[] startloaction = new int[2];
int[] endloaction = new int[2];
for (TransitionItem item : mTransitionItems) {
final View enterView = item.enterView;
final View exitView = item.exitView;
int[] startloaction = new int[2];
ViewUtils.getLocationInAncestor(exitView, startloaction, mExitScene.getSceneView());
int[] endloaction = new int[2];
ViewUtils.getLocationInAncestor(enterView, endloaction, mEnterScene.getSceneView());
ViewUtils.getLocationInAncestor(exitView, startloaction, mExitView);
ViewUtils.getLocationInAncestor(enterView, endloaction, mEnterView);
int startWidth = exitView.getWidth();
int startHeight = exitView.getHeight();
item.endWidth = enterView.getWidth();
item.endHeight = enterView.getHeight();
item.startWidth = startWidth;
item.startHeight = startHeight;
item.endloaction = endloaction;
Bitmap bmp = ViewUtils.getBitmapFromView(enterView);
View bmpView = new View(exitView.getContext());
@@ -355,70 +349,66 @@ public class TransitionCurtain extends Curtain {
bmpView.setBackgroundDrawable(new BitmapDrawable(bmp));
AbsoluteLayout.LayoutParams lp = new AbsoluteLayout.LayoutParams(
startWidth, startHeight, startloaction[0], startloaction[1]);
exitSceneView.addView(bmpView, lp);
mExitView.addView(bmpView, lp);
bmpView.setVisibility(View.INVISIBLE);
globalLayoutSet.addListenerToObserver(bmpView.getViewTreeObserver());
bmpView.setPivotX(0);
bmpView.setPivotY(0);
PropertyValuesHolder scaleXPvh = PropertyValuesHolder.ofFloat("scaleX", 1f, (float) enterView.getWidth() / startWidth);
PropertyValuesHolder scaleYPvh = PropertyValuesHolder.ofFloat("scaleY", 1f, (float) enterView.getHeight() / startHeight);
PropertyValuesHolder xPvh = PropertyValuesHolder.ofFloat("x", startloaction[0], endloaction[0]);
PropertyValuesHolder yPvh = PropertyValuesHolder.ofFloat("y", startloaction[1], endloaction[1]);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(bmpView, scaleXPvh, scaleYPvh, xPvh, yPvh);
anim.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
anim.setDuration(ANIMATE_TIME);
mAnimatorCollection.add(anim);
}
globalLayoutSet.setOnAllLayoutListener(new CloseSecondAllLayoutListener(
mEnterScene, mExitScene, transitionItemSet, mAnimatorCollection));
mStageLayout.addOnLayoutListener(new CloseSecondLayoutListener(mEnter, mExit, mTransitionItems, mAnimatorCollection));
}
}
private class CloseSecondAllLayoutListener implements GlobalLayoutSet.OnAllLayoutListener{
private class CloseSecondLayoutListener implements StageLayout.OnLayoutListener {
Scene mEnterScene;
Scene mExitScene;
Set<TransitionItem> mTransitionItemSet;
Set<Animator> mAnimatorCollection;
private Scene mEnter;
private Scene mExit;
private Set<TransitionItem> mTransitionItems;
private Set<Animator> mAnimatorCollection;
public CloseSecondAllLayoutListener(Scene enterScene, Scene exitScene,
Set<TransitionItem> transitionItemSet, Set<Animator> animatorCollection) {
mEnterScene = enterScene;
mExitScene = exitScene;
mTransitionItemSet = transitionItemSet;
public CloseSecondLayoutListener(Scene enter, Scene exit, Set<TransitionItem> transitionItems,
Set<Animator> animatorCollection) {
mEnter = enter;
mExit = exit;
mTransitionItems = transitionItems;
mAnimatorCollection = animatorCollection;
}
@Override
public void onAllLayout() {
Set<TransitionItem> transitionItemSet = mTransitionItemSet;
Set<Animator> animatorCollection = mAnimatorCollection;
public void onLayout(View view) {
mEnter.getStageLayout().removeOnLayoutListener(this);
for (TransitionItem item : transitionItemSet) {
for (TransitionItem item : mTransitionItems) {
View exitView = item.exitView;
View bmpView = item.bmpView;
exitView.setVisibility(View.INVISIBLE);
bmpView.setVisibility(View.VISIBLE);
bmpView.setPivotX(0);
bmpView.setPivotY(0);
PropertyValuesHolder scaleXPvh = PropertyValuesHolder.ofFloat("scaleX", (float) item.endWidth / item.startWidth);
PropertyValuesHolder scaleYPvh = PropertyValuesHolder.ofFloat("scaleY", (float) item.endHeight/ item.startHeight);
PropertyValuesHolder xPvh = PropertyValuesHolder.ofFloat("x", item.endloaction[0]);
PropertyValuesHolder yPvh = PropertyValuesHolder.ofFloat("y", item.endloaction[1]);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(bmpView, scaleXPvh, scaleYPvh, xPvh, yPvh);
anim.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
anim.setDuration(ANIMATE_TIME);
animatorCollection.add(anim);
}
mAnimatorSet = new AnimatorSet();
mAnimatorSet.playTogether(animatorCollection);
mAnimatorSet.playTogether(mAnimatorCollection);
mAnimatorSet.addListener(new SimpleAnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
dispatchCloseFinished(mEnterScene, mExitScene);
for (TransitionItem item : mTransitionItemSet) {
dispatchCloseFinished(mEnter, mExit);
ViewGroup exitSceneView = mExit.getSceneView();
for (TransitionItem item : mTransitionItems) {
item.enterView.setVisibility(View.VISIBLE);
item.exitView.setVisibility(View.VISIBLE);
ViewGroup exitSceneView = mExitScene.getSceneView();
exitSceneView.removeView(item.bmpView);
item.bmp.recycle();
@@ -447,15 +437,10 @@ public class TransitionCurtain extends Curtain {
View exitView;
Bitmap bmp;
View bmpView;
int[] endloaction;
int startWidth;
int startHeight;
int endWidth;
int endHeight;
}
/**
* the interface help {@link TransitionCurtain} to get the views to do transfer
* the interface help {@link ElementsSharedCurtain} to get the views to do transfer
*/
public interface ViewPairSet {
View[] getFromViewSet(@NonNull Scene fromScene);
@@ -0,0 +1,220 @@
/*
* Copyright (C) 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.scene;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.support.annotation.NonNull;
import android.view.View;
import com.hippo.animation.SimpleAnimatorListener;
import com.hippo.util.AnimationUtils;
import com.hippo.yorozuya.Say;
import java.util.HashSet;
import java.util.Set;
public class OffsetCurtain extends Curtain {
private static final String TAG = OffsetCurtain.class.getSimpleName();
private static long ANIMATE_TIME = 300L;
public static final int DIRECTION_LEFT = 0;
public static final int DIRECTION_TOP = 1;
public static final int DIRECTION_RIGHT = 2;
public static final int DIRECTION_BOTTOM = 3;
private int mDirection;
private float mOffsetPercent;
private AnimatorSet mAnimatorSet;
public OffsetCurtain(int direction) {
this(direction, 0.25f);
}
public OffsetCurtain(int direction, float offsetPercent) {
mDirection = direction;
mOffsetPercent = offsetPercent;
}
@Override
protected boolean needSpecifyPreviousScene() {
return false;
}
@Override
public void open(@NonNull final Scene enter, @NonNull final Scene exit) {
// Check stage layout isLayoutRequested
final StageLayout stageLayout = enter.getStageLayout();
if (!stageLayout.isLayoutRequested()) {
Say.w(TAG, "WTF? stageLayout.isLayoutRequested() == false");
dispatchOpenFinished(enter, exit);
return;
}
final View enterSceneView = enter.getSceneView();
enterSceneView.setVisibility(View.INVISIBLE);
stageLayout.addOnLayoutListener(new StageLayout.OnLayoutListener() {
@Override
public void onLayout(View view) {
stageLayout.removeOnLayoutListener(this);
final Set<Animator> animatorCollection = new HashSet<>();
int width = enterSceneView.getWidth();
int height = enterSceneView.getHeight();
float translationX = 0f;
float translationY = 0f;
switch (mDirection) {
case DIRECTION_LEFT:
translationX = -width * mOffsetPercent;
break;
case DIRECTION_TOP:
translationY = -height * mOffsetPercent;
break;
case DIRECTION_RIGHT:
translationX = width * mOffsetPercent;
break;
default:
case DIRECTION_BOTTOM:
translationY = height * mOffsetPercent;
break;
}
PropertyValuesHolder pvh;
if (translationX != 0f) {
pvh = PropertyValuesHolder.ofFloat("translationX", translationX, 0f);
} else {
pvh = PropertyValuesHolder.ofFloat("translationY", translationY, 0f);
}
Animator offsetAnimator = ObjectAnimator.ofPropertyValuesHolder(enterSceneView, pvh);
offsetAnimator.setDuration(ANIMATE_TIME);
offsetAnimator.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
animatorCollection.add(offsetAnimator);
View enterView = enter.getSceneView();
enterView.setAlpha(0f);
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(enterView, "alpha", 0f, 1f);
alphaAnim.setDuration(ANIMATE_TIME);
alphaAnim.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
animatorCollection.add(alphaAnim);
mAnimatorSet = new AnimatorSet();
mAnimatorSet.playTogether(animatorCollection);
mAnimatorSet.addListener(new SimpleAnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
enterSceneView.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
dispatchOpenFinished(enter, exit);
mAnimatorSet = null;
}
});
mAnimatorSet.start();
}
});
}
@Override
public void close(@NonNull final Scene enter, @NonNull final Scene exit) {
// Check stage layout isLayoutRequested
final StageLayout stageLayout = enter.getStageLayout();
if (stageLayout.isLayoutRequested()) {
stageLayout.addOnLayoutListener(new StageLayout.OnLayoutListener() {
@Override
public void onLayout(View view) {
stageLayout.removeOnLayoutListener(this);
onClose(enter, exit);
}
});
} else {
onClose(enter, exit);
}
}
private void onClose(final Scene enter, final Scene exit) {
final Set<Animator> animatorCollection = new HashSet<>();
final View exitSceneView = exit.getSceneView();
int width = exitSceneView.getWidth();
int height = exitSceneView.getHeight();
float translationX = 0f;
float translationY = 0f;
switch (mDirection) {
case DIRECTION_LEFT:
translationX = -width * mOffsetPercent;
break;
case DIRECTION_TOP:
translationY = -height * mOffsetPercent;
break;
case DIRECTION_RIGHT:
translationX = width * mOffsetPercent;
break;
default:
case DIRECTION_BOTTOM:
translationY = height * mOffsetPercent;
break;
}
PropertyValuesHolder pvh;
if (translationX != 0f) {
pvh = PropertyValuesHolder.ofFloat("translationX", 0f, translationX);
} else {
pvh = PropertyValuesHolder.ofFloat("translationY", 0f, translationY);
}
Animator offsetAnimator = ObjectAnimator.ofPropertyValuesHolder(exitSceneView, pvh);
offsetAnimator.setDuration(ANIMATE_TIME);
offsetAnimator.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR);
animatorCollection.add(offsetAnimator);
View exitView = exit.getSceneView();
exitView.setAlpha(1f);
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(exitView, "alpha", 1f, 0f);
alphaAnim.setDuration(ANIMATE_TIME);
alphaAnim.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR);
animatorCollection.add(alphaAnim);
mAnimatorSet = new AnimatorSet();
mAnimatorSet.playTogether(animatorCollection);
mAnimatorSet.addListener(new SimpleAnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
dispatchCloseFinished(enter, exit);
mAnimatorSet = null;
}
});
mAnimatorSet.start();
}
@Override
public void endAnimation() {
if (mAnimatorSet != null) {
mAnimatorSet.end();
}
}
@Override
public boolean isInAnimation() {
return mAnimatorSet != null;
}
}
@@ -6,12 +6,10 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewTreeObserver;
import com.hippo.animation.DurationInterpolator;
import com.hippo.animation.SimpleAnimatorListener;
import com.hippo.yorozuya.Say;
import com.hippo.yorozuya.ViewUtils;
import java.util.HashSet;
import java.util.Set;
@@ -33,6 +31,11 @@ public class RippleCurtain extends Curtain {
mStartY = startY;
}
@Override
protected boolean needSpecifyPreviousScene() {
return false;
}
@Override
protected void onRebirth() {
mStartX = 0;
@@ -41,16 +44,24 @@ public class RippleCurtain extends Curtain {
@Override
public void open(@NonNull final Scene enter, @NonNull final Scene exit) {
// Check stage layout isLayoutRequested
final StageLayout stageLayout = enter.getStageLayout();
if (!stageLayout.isLayoutRequested()) {
Say.w(TAG, "WTF? stageLayout.isLayoutRequested() == false");
dispatchOpenFinished(enter, exit);
return;
}
final Set<Animator> animatorCollection = new HashSet<>();
final SceneView enterView = enter.getSceneView();
enterView.setStartPoint(mStartX, mStartY);
enterView.setVisibility(View.INVISIBLE);
enterView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
stageLayout.addOnLayoutListener(new StageLayout.OnLayoutListener() {
@Override
public void onGlobalLayout() {
ViewUtils.removeOnGlobalLayoutListener(enterView.getViewTreeObserver(), this);
public void onLayout(View view) {
stageLayout.removeOnLayoutListener(this);
final View sceneContentView = enter.getSceneView().getChildAt(0);
if (sceneContentView != null) {
@@ -85,7 +96,6 @@ public class RippleCurtain extends Curtain {
@Override
public void onAnimationEnd(Animator animation) {
dispatchOpenFinished(enter, exit);
hideSceneOnOpen(exit);
mAnimatorSet = null;
}
});
@@ -96,8 +106,22 @@ public class RippleCurtain extends Curtain {
@Override
public void close(@NonNull final Scene enter, @NonNull final Scene exit) {
showSceneOnClose(enter);
// Check stage layout isLayoutRequested
final StageLayout stageLayout = enter.getStageLayout();
if (stageLayout.isLayoutRequested()) {
stageLayout.addOnLayoutListener(new StageLayout.OnLayoutListener() {
@Override
public void onLayout(View view) {
stageLayout.removeOnLayoutListener(this);
onClose(enter, exit);
}
});
} else {
onClose(enter, exit);
}
}
private void onClose(final Scene enter, final Scene exit) {
final Set<Animator> animatorCollection = new HashSet<>();
final SceneView exitView = exit.getSceneView();
+34 -20
View File
@@ -26,12 +26,10 @@ import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.ViewUtils;
/**
* {@link com.hippo.scene.Scene} is a {@code Activity} of {@link android.app.Activity}.
@@ -68,7 +66,7 @@ public abstract class Scene {
private int mId;
private boolean mHide;
private boolean mHide = false;
public void setSceneManager(SceneManager sceneManager) {
mSceneManager = sceneManager;
@@ -123,16 +121,20 @@ public abstract class Scene {
}
public void startScene(@NonNull Class sceneClass) {
mSceneManager.startScene(sceneClass, null, null);
startScene(sceneClass, null, null);
}
public void startScene(@NonNull Class sceneClass, @Nullable Announcer announcer) {
mSceneManager.startScene(sceneClass, announcer, null);
startScene(sceneClass, announcer, null);
}
public Curtain getDefaultCurtain() {
return new OffsetCurtain(OffsetCurtain.DIRECTION_BOTTOM);
}
public void startScene(@NonNull Class sceneClass, @Nullable Announcer announcer,
@Nullable Curtain curtain) {
mSceneManager.startScene(sceneClass, announcer, curtain);
mSceneManager.startScene(sceneClass, announcer, curtain == null ? getDefaultCurtain() : curtain);
}
int getState() {
@@ -148,20 +150,33 @@ public abstract class Scene {
}
public final void finish() {
mSceneManager.finishScene(this);
mSceneManager.finishScene(this, false);
}
/**
* Tell {@link SceneManager} how to {@link View#setVisibility(int)} when it is not the first scene.
* If it covers previous scene completely, {@link SceneManager} will setVisibility(View.INVISIBLE),
* otherwise setVisibility(View.VISIBLE)
*
* @return {@code true} if it covers previous scene completely
*/
protected boolean coverCompletely() {
return true;
}
void setHide(boolean hide) {
checkSceneView();
mHide = hide;
if (hide) {
mSceneView.setVisibility(View.GONE);
} else {
mSceneView.setVisibility(View.VISIBLE);
if (mHide != hide) {
mHide = hide;
if (hide) {
mSceneView.setVisibility(View.INVISIBLE);
} else {
mSceneView.setVisibility(View.VISIBLE);
}
}
}
boolean getHide() {
boolean isHide() {
return mHide;
}
@@ -197,13 +212,8 @@ public abstract class Scene {
// Hide it if it is hidden
if (mHide) {
mSceneView.setVisibility(View.INVISIBLE);
mSceneView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ViewUtils.removeOnGlobalLayoutListener(mSceneView.getViewTreeObserver(), this);
mSceneView.setVisibility(View.GONE);
}
});
} else {
mSceneView.setVisibility(View.VISIBLE);
}
}
@@ -397,6 +407,10 @@ public abstract class Scene {
}
}
public StageLayout getStageLayout() {
return getStageActivity().getStageLayout();
}
// Add scene view to stage layout
void attachToStage() {
if (mSceneView != null && !isInStage()) {
@@ -116,10 +116,14 @@ public class SceneManager {
}
}
private void checkPrepareToDie(Scene scene) {
/// Ture is finishScene is called
private boolean checkPrepareToDie(Scene scene) {
if (mPrepareToDieSceneSet.contains(scene)) {
mPrepareToDieSceneSet.remove(scene);
finishScene(scene);
finishScene(scene, true);
return true;
} else {
return false;
}
}
@@ -175,6 +179,20 @@ public class SceneManager {
startScene(dialog, curtain);
}
private void updateSceneVisibility() {
int pointer = mSceneStack.size() - 1;
for (; pointer >= 0; pointer--) {
Scene scene = mSceneStack.get(pointer);
scene.setHide(false);
if (scene.coverCompletely()) {
break;
}
}
for (pointer--; pointer >= 0; pointer--) {
mSceneStack.get(pointer).setHide(true);
}
}
private void startScene(Scene scene, Curtain curtain) {
Scene previousState = getTopScene();
mSceneStack.push(scene);
@@ -199,13 +217,18 @@ public class SceneManager {
scene.setFitPaddingBottom(fitPaddingBottom);
}
if (curtain != null && previousState != null) {
scene.setState(Scene.SCENE_STATE_OPEN);
curtain.open(scene, previousState);
// Set state run by curtain
} else {
scene.setState(Scene.SCENE_STATE_RUN);
checkPrepareToDie(scene);
scene.setState(Scene.SCENE_STATE_RUN);
if (!checkPrepareToDie(scene)) {
// No finish called. Go on
if (curtain != null && previousState != null) {
scene.setState(Scene.SCENE_STATE_OPEN);
curtain.open(scene, previousState);
} else {
// No curtain, set visiblity for previousState here
if (previousState != null) {
updateSceneVisibility();
}
}
}
}
@@ -228,7 +251,7 @@ public class SceneManager {
}
}
void finishScene(@NonNull Scene scene) {
void finishScene(@NonNull Scene scene, boolean noAnimation) {
checkLoop();
int index = getSceneIndex(scene);
@@ -248,7 +271,7 @@ public class SceneManager {
// TODO If the cene is under a dialog, the previous may not show
if (index != mSceneStack.size() - 1 || index == 0) {
// It is the last scene, just finish the activity
// It is not the first or it is the last scene
mSceneStack.remove(index);
scene.destroy(true);
scene.setState(Scene.SCENE_STATE_DESTROY);
@@ -256,7 +279,11 @@ public class SceneManager {
scene.setState(Scene.SCENE_STATE_DIE);
if (index == 0) {
// It is the last scene, just finish the activity
getStageActivity().finish();
} else {
// Update the scene under target scene visibility
updateSceneVisibility();
}
} else {
mSceneStack.remove(index);
@@ -267,9 +294,12 @@ public class SceneManager {
scene.destroy(true);
// Update scene visibility
updateSceneVisibility();
Curtain curtain = scene.getCurtain();
if (curtain != null &&
(!curtain.specifyPreviousScene() || curtain.isPreviousScene(previousScene))) {
if (!noAnimation && curtain != null &&
(!curtain.needSpecifyPreviousScene() || curtain.isPreviousScene(previousScene))) {
scene.setState(Scene.SCENE_STATE_CLOSE);
curtain.close(previousScene, scene);
// add the scene to legacy set
@@ -279,6 +309,8 @@ public class SceneManager {
scene.setState(Scene.SCENE_STATE_DESTROY);
scene.die(false);
scene.setState(Scene.SCENE_STATE_DIE);
previousScene.setHide(false);
}
}
} else {
@@ -296,6 +328,7 @@ public class SceneManager {
scene.open();
scene.setState(Scene.SCENE_STATE_RUN);
checkPrepareToDie(scene);
updateSceneVisibility();
}
void closeScene(Scene scene) {
@@ -1,159 +0,0 @@
/*
* Copyright (C) 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.scene;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewTreeObserver;
import com.hippo.animation.SimpleAnimatorListener;
import com.hippo.util.AnimationUtils;
import com.hippo.yorozuya.ViewUtils;
public class SimpleCurtain extends Curtain {
private static long ANIMATE_TIME = 300L;
public static final int DIRECTION_LEFT = 0;
public static final int DIRECTION_TOP = 1;
public static final int DIRECTION_RIGHT = 2;
public static final int DIRECTION_BOTTOM = 3;
private int mDirection;
private Animator mAnimator;
public SimpleCurtain(int direction) {
mDirection = direction;
}
@Override
public void open(@NonNull final Scene enter, @NonNull final Scene exit) {
final View enterSceneView = enter.getSceneView();
enterSceneView.setVisibility(View.INVISIBLE);
enterSceneView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ViewUtils.removeOnGlobalLayoutListener(enterSceneView.getViewTreeObserver(), this);
int width = enterSceneView.getWidth();
int height = enterSceneView.getHeight();
float translationX = 0f;
float translationY = 0f;
switch (mDirection) {
case DIRECTION_LEFT:
translationX = -width;
break;
case DIRECTION_TOP:
translationY = -height;
break;
case DIRECTION_RIGHT:
translationX = width;
break;
default:
case DIRECTION_BOTTOM:
translationY = height;
break;
}
PropertyValuesHolder pvh;
if (translationX != 0f) {
pvh = PropertyValuesHolder.ofFloat("translationX", translationX, 0f);
} else {
pvh = PropertyValuesHolder.ofFloat("translationY", translationY, 0f);
}
Animator animator = ObjectAnimator.ofPropertyValuesHolder(enterSceneView, pvh);
animator.setDuration(ANIMATE_TIME);
animator.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
animator.addListener(new SimpleAnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
enterSceneView.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
dispatchOpenFinished(enter, exit);
hideSceneOnOpen(exit);
mAnimator = null;
}
});
animator.start();
mAnimator = animator;
}
});
}
@Override
public void close(@NonNull final Scene enter, @NonNull final Scene exit) {
showSceneOnClose(enter);
final View exitSceneView = exit.getSceneView();
int width = exitSceneView.getWidth();
int height = exitSceneView.getHeight();
float translationX = 0f;
float translationY = 0f;
switch (mDirection) {
case DIRECTION_LEFT:
translationX = -width;
break;
case DIRECTION_TOP:
translationY = -height;
break;
case DIRECTION_RIGHT:
translationX = width;
break;
default:
case DIRECTION_BOTTOM:
translationY = height;
break;
}
PropertyValuesHolder pvh;
if (translationX != 0f) {
pvh = PropertyValuesHolder.ofFloat("translationX", 0f, translationX);
} else {
pvh = PropertyValuesHolder.ofFloat("translationY", 0f, translationY);
}
Animator animator = ObjectAnimator.ofPropertyValuesHolder(exitSceneView, pvh);
animator.setDuration(ANIMATE_TIME);
animator.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR);
animator.addListener(new SimpleAnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
dispatchCloseFinished(enter, exit);
mAnimator = null;
}
});
animator.start();
mAnimator = animator;
}
@Override
public void endAnimation() {
if (mAnimator != null) {
mAnimator.end();
}
}
@Override
public boolean isInAnimation() {
return mAnimator != null;
}
}
@@ -110,6 +110,11 @@ public class SimpleDialog extends SceneDialog implements View.OnClickListener,
}
}
@Override
protected boolean coverCompletely() {
return false;
}
private void bindDialog() {
ListView listView = mListView;
@@ -192,6 +197,10 @@ public class SimpleDialog extends SceneDialog implements View.OnClickListener,
mLayout.setFitPaddingBottom(b);
}
public ViewGroup getLayout() {
return mLayout;
}
public CardView getCushion() {
return mCushion;
}
@@ -22,20 +22,22 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewTreeObserver;
import com.hippo.animation.ArgbEvaluator;
import com.hippo.animation.SimpleAnimatorListener;
import com.hippo.util.AnimationUtils;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.ViewUtils;
import com.hippo.yorozuya.Say;
import java.util.HashSet;
import java.util.Set;
public class SimpleDialogCurtain extends Curtain {
private static final String TAG = SimpleDialogCurtain.class.getSimpleName();
private static long ANIMATE_TIME = 300L;
private static float SCALE_PERCENT = 0.75f;
private int mStartX;
private int mStartY;
@@ -57,14 +59,22 @@ public class SimpleDialogCurtain extends Curtain {
}
@Override
protected void onRebirth() {
mStartX = 0;
mStartY = 0;
protected boolean needSpecifyPreviousScene() {
return false;
}
@Override
public void open(@NonNull final Scene enter, @NonNull final Scene exit) {
AssertUtils.assertInstanceof("SimpleDialogCurtain should only use for SimpleDialog.", enter, SimpleDialog.class);
// Check stage layout isLayoutRequested
final StageLayout stageLayout = enter.getStageLayout();
if (!stageLayout.isLayoutRequested()) {
Say.w(TAG, "WTF? stageLayout.isLayoutRequested() == false");
dispatchOpenFinished(enter, exit);
return;
}
final SimpleDialog enterDialog = (SimpleDialog) enter;
final Set<Animator> animatorCollection = new HashSet<>();
@@ -87,13 +97,10 @@ public class SimpleDialogCurtain extends Curtain {
cushion.setVisibility(View.INVISIBLE);
frame.setVisibility(View.INVISIBLE);
// TODO addOnGlobalLayoutListener is not reliable
// TODO use isLayoutRequested
// TODO if isLayoutRequested return false, try to use StageView Size
frame.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
stageLayout.addOnLayoutListener(new StageLayout.OnLayoutListener() {
@Override
public void onGlobalLayout() {
ViewUtils.removeOnGlobalLayoutListener(frame.getViewTreeObserver(), this);
public void onLayout(View view) {
stageLayout.removeOnLayoutListener(this);
float floatStartX;
float floatStartY;
@@ -141,6 +148,7 @@ public class SimpleDialogCurtain extends Curtain {
cushion.setVisibility(View.VISIBLE);
frame.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
dispatchOpenFinished(enter, exit);
@@ -168,44 +176,48 @@ public class SimpleDialogCurtain extends Curtain {
animatorCollection.add(colorAnim);
colorAnim.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR);
final View layout = exitDialog.getLayout();
final View cushion = exitDialog.getCushion();
final View frame = exitDialog.getFrame();
AssertUtils.assertNotNull("Cushion view must not be null.", cushion);
AssertUtils.assertNotNull("Frame view must not be null.", frame);
float floatEndX;
float floatEndY;
int intEndX;
int intEndY;
if (mStartX == 0 && mStartY == 0) {
int[] center = new int[2];
exitDialog.getCenterLocation(center);
floatEndX = center[0];
floatEndY = center[1];
intEndX = center[0];
intEndY = center[1];
// Check stage layout isLayoutRequested
final StageLayout stageLayout = enter.getStageLayout();
if (stageLayout.isLayoutRequested()) {
stageLayout.addOnLayoutListener(new StageLayout.OnLayoutListener() {
@Override
public void onLayout(View view) {
onClose(enter, exitDialog, layout, cushion, frame, animatorCollection);
}
});
} else {
floatEndX = mStartX;
floatEndY = mStartY;
intEndX = mStartX;
intEndY = mStartY;
onClose(enter, exitDialog, layout, cushion, frame, animatorCollection);
}
}
cushion.setPivotX(0f);
cushion.setPivotY(0f);
PropertyValuesHolder scaleXPvh = PropertyValuesHolder.ofFloat("scaleX", 0f);
PropertyValuesHolder scaleYPvh = PropertyValuesHolder.ofFloat("scaleY", 0f);
PropertyValuesHolder xPvh = PropertyValuesHolder.ofFloat("x", floatEndX);
PropertyValuesHolder yPvh = PropertyValuesHolder.ofFloat("y", floatEndY);
ObjectAnimator animCushion = ObjectAnimator.ofPropertyValuesHolder(cushion, scaleXPvh, scaleYPvh, xPvh, yPvh);
private void onClose(final Scene enter, final SimpleDialog exitDialog, View layout, View cushion, View frame, Set<Animator> animatorCollection) {
layout.setAlpha(1f);
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(layout, "alpha", 1f, 0f);
alphaAnim.setDuration(ANIMATE_TIME);
animatorCollection.add(alphaAnim);
cushion.setPivotX(cushion.getWidth() / 2);
cushion.setPivotY(cushion.getHeight() / 2);
PropertyValuesHolder scaleXPvh = PropertyValuesHolder.ofFloat("scaleX", 1f, SCALE_PERCENT);
PropertyValuesHolder scaleYPvh = PropertyValuesHolder.ofFloat("scaleY", 1f, SCALE_PERCENT);
ObjectAnimator animCushion = ObjectAnimator.ofPropertyValuesHolder(cushion, scaleXPvh, scaleYPvh);
animCushion.setDuration(ANIMATE_TIME);
animCushion.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
animatorCollection.add(animCushion);
PropertyValuesHolder leftPvh = PropertyValuesHolder.ofInt("drawLeft", intEndX);
PropertyValuesHolder topPvh = PropertyValuesHolder.ofInt("drawTop", intEndY);
PropertyValuesHolder rightPvh = PropertyValuesHolder.ofInt("drawRight", intEndX);
PropertyValuesHolder bottomPvh = PropertyValuesHolder.ofInt("drawBottom", intEndY);
int dx = (int) (frame.getWidth() * (1 - SCALE_PERCENT) / 2);
int dy = (int) (frame.getHeight() * (1 - SCALE_PERCENT) / 2);
PropertyValuesHolder leftPvh = PropertyValuesHolder.ofInt("drawLeft", frame.getLeft() + dx);
PropertyValuesHolder topPvh = PropertyValuesHolder.ofInt("drawTop", frame.getTop() + dy);
PropertyValuesHolder rightPvh = PropertyValuesHolder.ofInt("drawRight", frame.getRight() - dx);
PropertyValuesHolder bottomPvh = PropertyValuesHolder.ofInt("drawBottom", frame.getBottom() - dy);
ObjectAnimator animFrame = ObjectAnimator.ofPropertyValuesHolder(frame, leftPvh, topPvh, rightPvh, bottomPvh);
animFrame.setDuration(ANIMATE_TIME);
animFrame.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
@@ -216,7 +228,7 @@ public class SimpleDialogCurtain extends Curtain {
mAnimatorSet.addListener(new SimpleAnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
dispatchCloseFinished(enter, exit);
dispatchCloseFinished(enter, exitDialog);
mAnimatorSet = null;
}
});
@@ -21,10 +21,17 @@ import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.List;
public class StageLayout extends FrameLayout {
private List<OnLayoutListener> mOnLayoutListeners = new ArrayList<>(3);
private List<OnLayoutListener> mOnLayoutListenersCopy = new ArrayList<>(3);
public StageLayout(Context context) {
super(context);
}
@@ -46,4 +53,31 @@ public class StageLayout extends FrameLayout {
protected void dispatchRestoreInstanceState(@NonNull SparseArray<Parcelable> container) {
// Do nothing
}
public void addOnLayoutListener(OnLayoutListener layoutListener) {
mOnLayoutListeners.add(layoutListener);
}
public void removeOnLayoutListener(OnLayoutListener layoutListener) {
mOnLayoutListeners.remove(layoutListener);
}
@Override
protected void onLayout(boolean changed, int l, int t,
int r, int b) {
super.onLayout(changed, l, t, r, b);
mOnLayoutListenersCopy.clear();
for (int i = 0, n = mOnLayoutListeners.size(); i < n; i++) {
mOnLayoutListenersCopy.add(mOnLayoutListeners.get(i));
}
for (OnLayoutListener listener : mOnLayoutListenersCopy) {
listener.onLayout(this);
}
}
public interface OnLayoutListener {
void onLayout(View view);
}
}
@@ -0,0 +1,70 @@
/*
* Copyright 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.scene;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
public class UnionCurtain extends Curtain {
private Curtain mOpenCurtain;
private Curtain mCloseCurtain;
public UnionCurtain(Curtain openCurtain, Curtain closeCurtain) {
mOpenCurtain = openCurtain;
mCloseCurtain = closeCurtain;
}
@Override
protected void setPreviousScene(@Nullable Scene pScene) {
super.setPreviousScene(pScene);
mOpenCurtain.setPreviousScene(pScene);
mCloseCurtain.setPreviousScene(pScene);
}
@Override
protected boolean needSpecifyPreviousScene() {
return mCloseCurtain.needSpecifyPreviousScene();
}
@Override
protected void onRebirth() {
mCloseCurtain.onRebirth();
}
@Override
public void open(@NonNull Scene enter, @NonNull Scene exit) {
mOpenCurtain.open(enter, exit);
}
@Override
public void close(@NonNull Scene enter, @NonNull Scene exit) {
mCloseCurtain.close(enter, exit);
}
@Override
public void endAnimation() {
mOpenCurtain.endAnimation();
mCloseCurtain.endAnimation();
}
@Override
public boolean isInAnimation() {
return mOpenCurtain.isInAnimation() || mCloseCurtain.isInAnimation();
}
}
@@ -28,7 +28,6 @@ import android.widget.TextView;
import com.hippo.ehviewer.R;
import com.hippo.rippleold.RippleSalon;
import com.hippo.scene.AppbarScene;
import com.hippo.scene.SimpleCurtain;
import com.hippo.scene.StageActivity;
import com.hippo.widget.recyclerview.EasyRecyclerView;
@@ -66,7 +65,7 @@ public class PreferenceHeaderScene extends AppbarScene {
if (clazz == null) {
return false;
} else {
startScene(clazz, null, new SimpleCurtain(SimpleCurtain.DIRECTION_BOTTOM));
startScene(clazz);
return true;
}
}
@@ -1,63 +0,0 @@
/*
* Copyright (C) 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.widget;
import android.view.ViewTreeObserver;
import com.hippo.yorozuya.ViewUtils;
public class GlobalLayoutSet {
private OnAllLayoutListener mOnAllLayoutListener;
private int mSize;
public void setOnAllLayoutListener(OnAllLayoutListener listener) {
mOnAllLayoutListener = listener;
}
public void addListenerToObserver(ViewTreeObserver observer) {
LayoutListener ll = new LayoutListener(observer);
observer.addOnGlobalLayoutListener(ll);
mSize++;
}
public int getSize() {
return mSize;
}
private class LayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
private ViewTreeObserver mObserver;
private LayoutListener(ViewTreeObserver observer) {
mObserver = observer;
}
@Override
public void onGlobalLayout() {
ViewUtils.removeOnGlobalLayoutListener(mObserver, this);
if (--mSize == 0) {
mOnAllLayoutListener.onAllLayout();
}
}
}
public interface OnAllLayoutListener {
void onAllLayout();
}
}