diff --git a/examples/src/main/java/com/alibaba/android/vlayout/example/VLayoutActivity.java b/examples/src/main/java/com/alibaba/android/vlayout/example/VLayoutActivity.java index d17b5522..318f3195 100644 --- a/examples/src/main/java/com/alibaba/android/vlayout/example/VLayoutActivity.java +++ b/examples/src/main/java/com/alibaba/android/vlayout/example/VLayoutActivity.java @@ -30,6 +30,7 @@ import com.alibaba.android.vlayout.VirtualLayoutManager; import com.alibaba.android.vlayout.VirtualLayoutManager.LayoutParams; import com.alibaba.android.vlayout.extend.PerformanceMonitor; +import com.alibaba.android.vlayout.extend.ViewLifeCycleListener; import com.alibaba.android.vlayout.layout.ColumnLayoutHelper; import com.alibaba.android.vlayout.layout.FixLayoutHelper; import com.alibaba.android.vlayout.layout.FloatLayoutHelper; @@ -110,8 +111,8 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); - mSwipeRefreshLayout = (SwipeRefreshLayout)findViewById(R.id.swipe_container); -; + mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container); + ; mFirstText = (TextView) findViewById(R.id.first); mLastText = (TextView) findViewById(R.id.last); mCountText = (TextView) findViewById(R.id.count); @@ -151,8 +152,6 @@ public void onScrolled(RecyclerView recyclerView, int i, int i2) { } }); - layoutManager.setRecycleOffset(300); - recyclerView.setLayoutManager(layoutManager); // layoutManager.setReverseLayout(true); @@ -173,6 +172,31 @@ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, Recycle viewPool.setMaxRecycledViews(0, 20); + layoutManager.setRecycleOffset(300); + + // viewLifeCycleListener should be used with setRecycleOffset() + layoutManager.setViewLifeCycleListener(new ViewLifeCycleListener() { + @Override + public void onAppearing(View view) { +// Log.e("ViewLifeCycleTest", "onAppearing: " + view); + } + + @Override + public void onDisappearing(View view) { +// Log.e("ViewLifeCycleTest", "onDisappearing: " + view); + } + + @Override + public void onAppeared(View view) { +// Log.e("ViewLifeCycleTest", "onAppeared: " + view); + } + + @Override + public void onDisappeared(View view) { +// Log.e("ViewLifeCycleTest", "onDisappeared: " + view); + } + }); + final DelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager, true); recyclerView.setAdapter(delegateAdapter); @@ -280,7 +304,7 @@ public void onBindViewHolder(MainViewHolder holder, int position) { }); } - { + { RangeGridLayoutHelper layoutHelper = new RangeGridLayoutHelper(4); layoutHelper.setBgColor(Color.GREEN); layoutHelper.setWeights(new float[]{20f, 26.665f}); @@ -355,7 +379,7 @@ public void onBindViewHolder(MainViewHolder holder, int position) { adapters.add(new SubAdapter(this, layoutHelper, 23)); } - + { SingleLayoutHelper layoutHelper = new SingleLayoutHelper(); layoutHelper.setBgColor(Color.BLUE); @@ -632,7 +656,7 @@ public void onBindViewHolder(MainViewHolder holder, int position) { } adapters.add( - new FooterAdapter(recyclerView, VLayoutActivity.this, new GridLayoutHelper(1), 1)); + new FooterAdapter(recyclerView, VLayoutActivity.this, new GridLayoutHelper(1), 1)); delegateAdapter.setAdapters(adapters); @@ -642,8 +666,8 @@ public void onBindViewHolder(MainViewHolder holder, int position) { trigger = new Runnable() { @Override public void run() { - //recyclerView.scrollToPosition(22); - //recyclerView.getAdapter().notifyDataSetChanged(); + //recyclerView.scrollToPosition(22); + //recyclerView.getAdapter().notifyDataSetChanged(); //mainHandler.postDelayed(trigger, 1000); //List newAdapters = new ArrayList<>(); //newAdapters.add((new SubAdapter(VLayoutActivity.this, new ColumnLayoutHelper(), 3))); @@ -674,7 +698,6 @@ public void onClick(View v) { }); - mainHandler.postDelayed(trigger, 1000); mSwipeRefreshLayout.setOnRefreshListener(new OnRefreshListener() { diff --git a/vlayout/src/main/java/com/alibaba/android/vlayout/ExposeLinearLayoutManagerEx.java b/vlayout/src/main/java/com/alibaba/android/vlayout/ExposeLinearLayoutManagerEx.java index 59ca1ca5..b569c353 100644 --- a/vlayout/src/main/java/com/alibaba/android/vlayout/ExposeLinearLayoutManagerEx.java +++ b/vlayout/src/main/java/com/alibaba/android/vlayout/ExposeLinearLayoutManagerEx.java @@ -129,7 +129,7 @@ class ExposeLinearLayoutManagerEx extends LinearLayoutManager { private final Method mEnsureLayoutStateMethod; - private int recycleOffset; + protected int recycleOffset; /** * Creates a vertical LinearLayoutManager @@ -1155,8 +1155,7 @@ protected int fill(RecyclerView.Recycler recycler, LayoutState layoutState, } recycleByLayoutStateExpose(recycler, layoutState); } - int remainingSpace = layoutState.mAvailable + layoutState.mExtra + ( - layoutState.mLayoutDirection == LayoutState.LAYOUT_START ? 0 : recycleOffset); //FIXME opt here to fix bg and shake + int remainingSpace = layoutState.mAvailable + layoutState.mExtra + recycleOffset; while (remainingSpace > 0 && layoutState.hasMore(state)) { layoutChunkResultCache.resetInternal(); layoutChunk(recycler, state, layoutState, layoutChunkResultCache); diff --git a/vlayout/src/main/java/com/alibaba/android/vlayout/VirtualLayoutManager.java b/vlayout/src/main/java/com/alibaba/android/vlayout/VirtualLayoutManager.java index 9e1cf60a..13a9c96d 100644 --- a/vlayout/src/main/java/com/alibaba/android/vlayout/VirtualLayoutManager.java +++ b/vlayout/src/main/java/com/alibaba/android/vlayout/VirtualLayoutManager.java @@ -24,15 +24,9 @@ package com.alibaba.android.vlayout; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - import com.alibaba.android.vlayout.extend.PerformanceMonitor; +import com.alibaba.android.vlayout.extend.ViewLifeCycleHelper; +import com.alibaba.android.vlayout.extend.ViewLifeCycleListener; import com.alibaba.android.vlayout.layout.BaseLayoutHelper; import com.alibaba.android.vlayout.layout.DefaultLayoutHelper; import com.alibaba.android.vlayout.layout.FixAreaAdjuster; @@ -53,6 +47,14 @@ import android.view.ViewGroup; import android.view.ViewParent; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + /** * A {@link android.support.v7.widget.RecyclerView.LayoutManager} implementation which provides @@ -103,6 +105,8 @@ public static void enableDebugging(boolean isDebug) { private PerformanceMonitor mPerformanceMonitor; + private ViewLifeCycleHelper mViewLifeCycleHelper; + public VirtualLayoutManager(@NonNull final Context context) { this(context, VERTICAL); } @@ -456,6 +460,10 @@ private void runPostLayout(RecyclerView.Recycler recycler, RecyclerView.State st } } } + + if (null != mViewLifeCycleHelper) { + mViewLifeCycleHelper.checkViewStatusInScreen(); + } } } @@ -625,6 +633,26 @@ public void offsetChildrenVertical(int dy) { LayoutHelper layoutHelper = layoutHelpers.get(i); layoutHelper.onOffsetChildrenVertical(dy, this); } + + if (null != mViewLifeCycleHelper) { + mViewLifeCycleHelper.checkViewStatusInScreen(); + } + } + + public void setViewLifeCycleListener(@NonNull ViewLifeCycleListener viewLifeCycleListener) { + if (null == viewLifeCycleListener) { + throw new IllegalArgumentException("ViewLifeCycleListener should not be null!"); + } + + if (recycleOffset == 0) { + throw new IllegalArgumentException("ViewLifeCycleListener should work with virtualLayoutManager.setRecycleOffset()!"); + } + + mViewLifeCycleHelper = new ViewLifeCycleHelper(this, viewLifeCycleListener); + } + + public int getVirtualLayoutDirection() { + return mLayoutState.mLayoutDirection; } private LayoutStateWrapper mTempLayoutStateWrapper = new LayoutStateWrapper(); @@ -1438,7 +1466,7 @@ private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int if (getOrientation() == VERTICAL) { widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mDecorInsets.left, - lp.rightMargin + mDecorInsets.right); + lp.rightMargin + mDecorInsets.right); } if (getOrientation() == HORIZONTAL) { heightSpec = updateSpecWithExtra(heightSpec, mDecorInsets.top, diff --git a/vlayout/src/main/java/com/alibaba/android/vlayout/extend/ViewLifeCycleHelper.java b/vlayout/src/main/java/com/alibaba/android/vlayout/extend/ViewLifeCycleHelper.java new file mode 100644 index 00000000..6a248d42 --- /dev/null +++ b/vlayout/src/main/java/com/alibaba/android/vlayout/extend/ViewLifeCycleHelper.java @@ -0,0 +1,142 @@ +package com.alibaba.android.vlayout.extend; + +import android.support.annotation.NonNull; +import android.support.v4.util.ArrayMap; +import android.view.View; + +import com.alibaba.android.vlayout.VirtualLayoutManager; + +public class ViewLifeCycleHelper { + public enum STATUS { + APPEARING, + APPEARED, + DISAPPEARING, + DISAPPEARED + } + + private ArrayMap mViewStatusMap = new ArrayMap<>(); + + private ViewLifeCycleListener mViewLifeCycleListener; + + private VirtualLayoutManager mVirtualLayoutManager; + + private int scrHeight; + + public ViewLifeCycleHelper(VirtualLayoutManager virtualLayoutManager, @NonNull ViewLifeCycleListener mViewLifeCycleListener) { + this.mViewLifeCycleListener = mViewLifeCycleListener; + this.mVirtualLayoutManager = virtualLayoutManager; + } + + public void checkViewStatusInScreen() { + for (int i = 0; i < mVirtualLayoutManager.getChildCount(); i++) { + View view = mVirtualLayoutManager.getChildAt(i); + if (scrHeight == 0) { + scrHeight = view.getContext().getResources().getDisplayMetrics().heightPixels; + } + + if (mVirtualLayoutManager.getVirtualLayoutDirection() == VirtualLayoutManager.LayoutState.LAYOUT_END) { + if (view.getTop() <= 0 && view.getBottom() >= 0 && isViewReadyDisAppearing(view)) { + setViewDisappearing(view); + } else if (view.getTop() <= scrHeight && view.getBottom() >= scrHeight && isViewReadyAppearing(view)) { + setViewAppearing(view); + } + } else { + if (view.getTop() <= 0 && view.getBottom() >= 0 && isViewReadyAppearing(view)) { + setViewAppearing(view); + } else if (view.getTop() <= scrHeight && view.getBottom() >= scrHeight && isViewReadyDisAppearing(view)) { + setViewDisappearing(view); + } + } + + if (view.getTop() >= 0 && view.getBottom() <= scrHeight) { + // fully in screen + + if (isViewReadyAppearing(view)) { + setViewAppearing(view); + + } else if (isViewReadyAppeared(view)) { + setViewAppeared(view); + } + } else if (view.getBottom() <= 0 || view.getTop() >= scrHeight) { + // not in screen + if (isViewReadyDisAppearing(view)) { + setViewDisappearing(view); + + } else if (isViewReadyDisAppeared(view)) { + setViewDisappeared(view); + } + + } + } + } + + private STATUS getViewStatus(View view) { + if (!mViewStatusMap.containsKey(view)) { + mViewStatusMap.put(view, STATUS.DISAPPEARED); + return STATUS.DISAPPEARED; + } + return mViewStatusMap.get(view); + } + + private void setViewstatus(View view, STATUS status) { + mViewStatusMap.put(view, status); + } + + private boolean isViewReadyAppearing(View view) { + return getViewStatus(view) == STATUS.DISAPPEARED; + } + + private void setViewAppearing(View view) { + if (getViewStatus(view) == STATUS.APPEARING) { + return; + } + + setViewstatus(view, STATUS.APPEARING); + if (null != mViewLifeCycleListener) { + mViewLifeCycleListener.onAppearing(view); + } + } + + private boolean isViewReadyAppeared(View view) { + return getViewStatus(view) == STATUS.APPEARING; + } + + private void setViewAppeared(View view) { + if (getViewStatus(view) == STATUS.APPEARED) { + return; + } + setViewstatus(view, STATUS.APPEARED); + if (null != mViewLifeCycleListener) { + mViewLifeCycleListener.onAppeared(view); + } + } + + private boolean isViewReadyDisAppearing(View view) { + return getViewStatus(view) == STATUS.APPEARED; + } + + private void setViewDisappearing(View view) { + if (getViewStatus(view) == STATUS.DISAPPEARING) { + return; + } + + setViewstatus(view, STATUS.DISAPPEARING); + if (null != mViewLifeCycleListener) { + mViewLifeCycleListener.onDisappearing(view); + } + } + + private boolean isViewReadyDisAppeared(View view) { + return getViewStatus(view) == STATUS.DISAPPEARING; + } + + private void setViewDisappeared(View view) { + if (getViewStatus(view) == STATUS.DISAPPEARED) { + return; + } + setViewstatus(view, STATUS.DISAPPEARED); + if (null != mViewLifeCycleListener) { + mViewLifeCycleListener.onDisappeared(view); + } + } +} diff --git a/vlayout/src/main/java/com/alibaba/android/vlayout/extend/ViewLifeCycleListener.java b/vlayout/src/main/java/com/alibaba/android/vlayout/extend/ViewLifeCycleListener.java new file mode 100644 index 00000000..21e12114 --- /dev/null +++ b/vlayout/src/main/java/com/alibaba/android/vlayout/extend/ViewLifeCycleListener.java @@ -0,0 +1,13 @@ +package com.alibaba.android.vlayout.extend; + +import android.view.View; + +public interface ViewLifeCycleListener { + void onAppearing(View view); + + void onDisappearing(View view); + + void onAppeared(View view); + + void onDisappeared(View view); +} diff --git a/vlayout/src/main/java/com/alibaba/android/vlayout/layout/StickyLayoutHelper.java b/vlayout/src/main/java/com/alibaba/android/vlayout/layout/StickyLayoutHelper.java index 8dad360d..cc35fecb 100644 --- a/vlayout/src/main/java/com/alibaba/android/vlayout/layout/StickyLayoutHelper.java +++ b/vlayout/src/main/java/com/alibaba/android/vlayout/layout/StickyLayoutHelper.java @@ -291,6 +291,10 @@ public void afterLayout(RecyclerView.Recycler recycler, RecyclerView.State state } else { fixLayoutStateInCase2(orientationHelper, recycler, startPosition, endPosition, helper); } + + if (mFixView != null){ + mFixView.setBackgroundColor(mBgColor); + } } private void fixLayoutStateFromAbnormal2Normal(OrientationHelperEx orientationHelper, RecyclerView.Recycler recycler, int startPosition, int endPosition,