提交 80dbe9d5 编写于 作者: T Takeshi Hagikura

Merge pull request #48 from google/avoid_object_creation

Avoid object creations in the onMeasure method.
......@@ -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.
先完成此消息的编辑!
想要评论请 注册