提交 e8083a52 编写于 作者: T Takeshi Hagikura

Avoid object creations in the onMeasure method.

(avoid reordering the views in every onMeasure method even if any order
attributres haven't changed from the last measurement)

Instead, this PR changes the behavior to reorder the views if a new view
is added or order attributes of any children has changed from the last
measurement.

Change-Id: Ied25bdbcb0da93a96445e4225eac7fee6b93cff9
上级 a085da39
......@@ -532,8 +532,6 @@ public class MainActivity extends AppCompatActivity
View view = mFlexboxLayout.getChildAt(flexItem.index);
FlexboxLayout.LayoutParams lp = flexItem.toLayoutParams(MainActivity.this);
view.setLayoutParams(lp);
// TODO: Update the layout only related views
mFlexboxLayout.requestLayout();
}
}
......
......@@ -157,6 +157,60 @@ public class FlexboxAndroidTest {
is(String.valueOf(1)));
}
@Test
@FlakyTest(tolerance = TOLERANCE)
public void testChangeOrder_fromChildSetLayoutParams() throws Throwable {
final FlexboxTestActivity activity = mActivityRule.getActivity();
mActivityRule.runOnUiThread(new Runnable() {
@Override
public void run() {
activity.setContentView(R.layout.activity_order_test);
}
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
final FlexboxLayout flexboxLayout = (FlexboxLayout) activity
.findViewById(R.id.flexbox_layout);
assertThat(flexboxLayout.getChildCount(), is(4));
// order: -1, index 1
assertThat(((TextView) flexboxLayout.getReorderedChildAt(0)).getText().toString(),
is(String.valueOf(2)));
// order: 0, index 2
assertThat(((TextView) flexboxLayout.getReorderedChildAt(1)).getText().toString(),
is(String.valueOf(3)));
// order: 1, index 3
assertThat(((TextView) flexboxLayout.getReorderedChildAt(2)).getText().toString(),
is(String.valueOf(4)));
// order: 2, index 0
assertThat(((TextView) flexboxLayout.getReorderedChildAt(3)).getText().toString(),
is(String.valueOf(1)));
// By changing the order and calling the setLayoutParams, the reordered array in the
// FlexboxLayout (mReordereredIndices) will be recreated without adding a new View.
mActivityRule.runOnUiThread(new Runnable() {
@Override
public void run() {
View view1 = flexboxLayout.getChildAt(0);
FlexboxLayout.LayoutParams lp = (FlexboxLayout.LayoutParams)
view1.getLayoutParams();
lp.order = -3;
view1.setLayoutParams(lp);
}
});
// order: -3, index 0
assertThat(((TextView) flexboxLayout.getReorderedChildAt(3)).getText().toString(),
is(String.valueOf(1)));
// order: -1, index 1
assertThat(((TextView) flexboxLayout.getReorderedChildAt(0)).getText().toString(),
is(String.valueOf(2)));
// order: 0, index 2
assertThat(((TextView) flexboxLayout.getReorderedChildAt(1)).getText().toString(),
is(String.valueOf(3)));
// order: 1, index 3
assertThat(((TextView) flexboxLayout.getReorderedChildAt(2)).getText().toString(),
is(String.valueOf(4)));
}
@Test
@FlakyTest(tolerance = TOLERANCE)
public void testFlexWrap_wrap() throws Throwable {
......
......@@ -22,6 +22,7 @@ import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
......@@ -30,9 +31,8 @@ import android.widget.RelativeLayout;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* A layout that arranges its children in a way its attributes can be specified like the
......@@ -210,6 +210,13 @@ public class FlexboxLayout extends ViewGroup {
*/
private int[] mReorderedIndices;
/**
* Caches the {@link LayoutParams#order} attributes for children views.
* Key: the index of the view ({@link #mReorderedIndices} isn't taken into account)
* Value: the value for the order attribute
*/
private SparseIntArray mOrderCache;
private List<FlexLine> mFlexLines = new ArrayList<>();
public FlexboxLayout(Context context) {
......@@ -237,7 +244,9 @@ public class FlexboxLayout extends ViewGroup {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mReorderedIndices = createReorderedIndices();
if (isOrderChangedFromLastMeasurement()) {
mReorderedIndices = createReorderedIndices();
}
// TODO: Only calculate the children views which are affected from the last measure.
switch (mFlexDirection) {
......@@ -271,31 +280,122 @@ public class FlexboxLayout extends ViewGroup {
return getChildAt(mReorderedIndices[index]);
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
// Create an array for the reordered indices before the View is added in the parent
// ViewGroup since otherwise reordered indices won't be in effect before the
// FlexboxLayout's onMeasure is called.
// Because requestLayout is requested in the super.addView method.
mReorderedIndices = createReorderedIndices(child, index, params);
super.addView(child, index, params);
}
/**
* Create an array, which indicates the reordered indices that {@link LayoutParams#order}
* attributes are taken into account.
* attributes are taken into account. This method takes a View before that is added as the
* parent ViewGroup's children.
*
* @param viewBeforeAdded the View instance before added to the array of children
* Views of the parent ViewGroup
* @param indexForViewBeforeAdded the index for the View before added to the array of the
* parent ViewGroup
* @param paramsForViewBeforeAdded the layout parameters for the View before added to the array
* of the parent ViewGroup
* @return an array which have the reordered indices
*/
private int[] createReorderedIndices(View viewBeforeAdded, int indexForViewBeforeAdded,
ViewGroup.LayoutParams paramsForViewBeforeAdded) {
int childCount = getChildCount();
List<Order> orders = createOrders(childCount);
Order orderForViewToBeAdded = new Order();
if (viewBeforeAdded != null
&& paramsForViewBeforeAdded instanceof FlexboxLayout.LayoutParams) {
orderForViewToBeAdded.order = ((LayoutParams) paramsForViewBeforeAdded).order;
} else {
orderForViewToBeAdded.order = LayoutParams.ORDER_DEFAULT;
}
if (indexForViewBeforeAdded == -1 || indexForViewBeforeAdded == childCount) {
orderForViewToBeAdded.index = childCount;
} else if (indexForViewBeforeAdded < getChildCount()) {
orderForViewToBeAdded.index = indexForViewBeforeAdded;
for (int i = indexForViewBeforeAdded; i < childCount; i++) {
orders.get(i).index++;
}
} else {
// This path is not expected since OutOfBoundException will be thrown in the ViewGroup
// But setting the index for fail-safe
orderForViewToBeAdded.index = childCount;
}
orders.add(orderForViewToBeAdded);
return sortOrdersIntoReorderedIndices(childCount + 1, orders);
}
/**
* Create an array, which indicates the reordered indices that {@link LayoutParams#order}
* attributes are taken into account.
*
* @return @return an array which have the reordered indices
*/
private int[] createReorderedIndices() {
int[] reorderedIndex = new int[getChildCount()];
int count = getChildCount();
SortedSet<Order> orderSet = new TreeSet<>();
for (int i = 0; i < count; i++) {
int childCount = getChildCount();
List<Order> orders = createOrders(childCount);
return sortOrdersIntoReorderedIndices(childCount, orders);
}
private int[] sortOrdersIntoReorderedIndices(int childCount, List<Order> orders) {
Collections.sort(orders);
if (mOrderCache == null) {
mOrderCache = new SparseIntArray(childCount);
}
mOrderCache.clear();
int[] reorderedIndices = new int[childCount];
int i = 0;
for (Order order : orders) {
reorderedIndices[i] = order.index;
mOrderCache.append(i, order.order);
i++;
}
return reorderedIndices;
}
@NonNull
private List<Order> createOrders(int childCount) {
List<Order> orders = new ArrayList<>();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
LayoutParams params = (LayoutParams) child.getLayoutParams();
Order order = new Order();
order.order = params.order;
order.index = i;
orderSet.add(order);
orders.add(order);
}
return orders;
}
int i = 0;
for (Order order : orderSet) {
reorderedIndex[i] = order.index;
i++;
/**
* Returns if any of the children's {@link LayoutParams#order} attributes are changed
* from the last measurement.
*
* @return {@code true} if changed from the last measurement, {@code false} otherwise.
*/
private boolean isOrderChangedFromLastMeasurement() {
int childCount = getChildCount();
if (mOrderCache.size() != childCount) {
return true;
}
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
if (view == null) {
continue;
}
LayoutParams lp = (LayoutParams) view.getLayoutParams();
if (lp.order != mOrderCache.get(i)) {
return true;
}
}
return reorderedIndex;
return false;
}
/**
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册