提交 3a8f88f8 编写于 作者: T Takeshi Hagikura

Implement add/remove a flex item from the demo app.

Restore the state across re-creation of the Activity such as
configuration changes.

Change-Id: I03bfe97d0a99137d54a426f0d5ea19bc9069c28f
上级 07e770fa
......@@ -26,6 +26,8 @@ android {
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
......@@ -36,8 +38,13 @@ android {
}
dependencies {
testCompile "junit:junit:${rootProject.ext.junitVersion}"
compile project(":library")
compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
compile "com.android.support:design:${rootProject.ext.supportLibVersion}"
compile project(":library")
testCompile "junit:junit:${rootProject.ext.junitVersion}"
androidTestCompile "com.android.support:support-annotations:${rootProject.ext.supportLibVersion}"
androidTestCompile "com.android.support.test:runner:${rootProject.ext.testRunnerVersion}"
androidTestCompile "com.android.support.test.espresso:espresso-core:${rootProject.ext.espressoVersion}"
}
/*
* Copyright 2016 Google Inc. All rights reserved.
*
* 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.google.android.apps.flexbox.test;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static junit.framework.Assert.assertNotNull;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import com.google.android.apps.flexbox.MainActivity;
import com.google.android.apps.flexbox.R;
import com.google.android.libraries.flexbox.FlexboxLayout;
import android.content.pm.ActivityInfo;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Integration tests for {@link MainActivity}.
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
public class MainActivityTest {
@Rule
public ActivityTestRule<MainActivity> mActivityRule =
new ActivityTestRule<>(MainActivity.class);
@Test
public void testAddFlexItem() {
MainActivity activity = mActivityRule.getActivity();
FlexboxLayout flexboxLayout = (FlexboxLayout) activity.findViewById(R.id.flexbox_layout);
assertNotNull(flexboxLayout);
int beforeCount = flexboxLayout.getChildCount();
onView(withId(R.id.add_fab)).perform(click());
assertThat(flexboxLayout.getChildCount(), is(beforeCount + 1));
}
@Test
public void testRemoveFlexItem() {
MainActivity activity = mActivityRule.getActivity();
FlexboxLayout flexboxLayout = (FlexboxLayout) activity.findViewById(R.id.flexbox_layout);
assertNotNull(flexboxLayout);
int beforeCount = flexboxLayout.getChildCount();
onView(withId(R.id.remove_fab)).perform(click());
assertThat(flexboxLayout.getChildCount(), is(beforeCount - 1));
}
@Test
public void testConfigurationChange() {
MainActivity activity = mActivityRule.getActivity();
FlexboxLayout flexboxLayout = (FlexboxLayout) activity.findViewById(R.id.flexbox_layout);
assertNotNull(flexboxLayout);
onView(withId(R.id.add_fab)).perform(click());
onView(withId(R.id.add_fab)).perform(click());
int beforeCount = flexboxLayout.getChildCount();
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
// Verify the flex items are restored across the configuration change.
assertThat(flexboxLayout.getChildCount(), is(beforeCount));
}
}
/*
* Copyright 2016 Google Inc. All rights reserved.
*
* 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.google.android.apps.flexbox;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Entity class representing a single flex item in the demo app.
*/
public class FlexItem implements Parcelable {
public int index;
/** minimum width in DP or -1 (MATCH_PARENT) or -2 (WRAP_CONTENT) */
public int minWidth;
/** minimum height in DP or -1 (MATCH_PARENT) or -2 (WRAP_CONTENT) */
public int minHeight;
public int topMargin;
public int startMargin;
public int endMargin;
public int bottomMargin;
public int paddingTop;
public int paddingStart;
public int paddingEnd;
public int paddingBottom;
public int order;
public int flexGrow;
public int flexShrink;
public int alignSelf;
public FlexItem() {}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.index);
dest.writeInt(this.minWidth);
dest.writeInt(this.minHeight);
dest.writeInt(this.topMargin);
dest.writeInt(this.startMargin);
dest.writeInt(this.endMargin);
dest.writeInt(this.bottomMargin);
dest.writeInt(this.paddingTop);
dest.writeInt(this.paddingStart);
dest.writeInt(this.paddingEnd);
dest.writeInt(this.paddingBottom);
dest.writeInt(this.order);
dest.writeInt(this.flexGrow);
dest.writeInt(this.flexShrink);
dest.writeInt(this.alignSelf);
}
protected FlexItem(Parcel in) {
this.index = in.readInt();
this.minWidth = in.readInt();
this.minHeight = in.readInt();
this.topMargin = in.readInt();
this.startMargin = in.readInt();
this.endMargin = in.readInt();
this.bottomMargin = in.readInt();
this.paddingTop = in.readInt();
this.paddingStart = in.readInt();
this.paddingEnd = in.readInt();
this.paddingBottom = in.readInt();
this.order = in.readInt();
this.flexGrow = in.readInt();
this.flexShrink = in.readInt();
this.alignSelf = in.readInt();
}
public static final Creator<FlexItem> CREATOR = new Creator<FlexItem>() {
@Override
public FlexItem createFromParcel(Parcel source) {
return new FlexItem(source);
}
@Override
public FlexItem[] newArray(int size) {
return new FlexItem[size];
}
};
}
......@@ -16,18 +16,34 @@
package com.google.android.apps.flexbox;
import com.google.android.libraries.flexbox.FlexboxLayout;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import java.security.SecureRandom;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
private static final String FLEX_ITEMS_KEY = "flex_items";
private FlexboxLayout mFlexboxLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......@@ -39,16 +55,141 @@ public class MainActivity extends AppCompatActivity
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open,
R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
if (drawer != null) {
drawer.setDrawerListener(toggle);
}
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
Menu navigationMenu = navigationView.getMenu();
if (navigationView != null) {
navigationView.setNavigationItemSelectedListener(this);
Menu navigationMenu = navigationView.getMenu();
}
mFlexboxLayout = (FlexboxLayout) findViewById(R.id.flexbox_layout);
if (savedInstanceState != null) {
ArrayList<FlexItem> flexItems = savedInstanceState
.getParcelableArrayList(FLEX_ITEMS_KEY);
assert flexItems != null;
mFlexboxLayout.removeAllViews();
for (int i = 0; i < flexItems.size(); i++) {
FlexItem flexItem = flexItems.get(i);
FlexboxLayout.LayoutParams lp = new FlexboxLayout.LayoutParams(
Util.dpToPixel(this, flexItem.minWidth),
Util.dpToPixel(this, flexItem.minHeight));
lp.order = flexItem.order;
lp.flexGrow = flexItem.flexGrow;
lp.flexShrink = flexItem.flexShrink;
lp.alignSelf = flexItem.alignSelf;
lp.topMargin = flexItem.topMargin;
lp.setMarginStart(flexItem.startMargin);
lp.setMarginEnd(flexItem.endMargin);
lp.bottomMargin = flexItem.bottomMargin;
TextView textView = createBaseFlexItemTextView(i);
ViewCompat.setPaddingRelative(textView, flexItem.paddingStart, flexItem.paddingTop,
flexItem.paddingEnd, flexItem.paddingBottom);
textView.setLayoutParams(lp);
mFlexboxLayout.addView(textView);
}
}
FloatingActionButton addFab = (FloatingActionButton) findViewById(R.id.add_fab);
if (addFab != null) {
addFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int viewIndex = mFlexboxLayout.getChildCount();
// index starts from 0. New View's index is N if N views ([0, 1, 2, ... N-1])
// exist.
TextView textView = createBaseFlexItemTextView(viewIndex);
int height = (int) getResources().getDimension(
R.dimen.flex_item_length);
int width = getRandomFlexItemWidth(getResources(), height);
FlexboxLayout.LayoutParams lp = new FlexboxLayout.LayoutParams(
width, height);
textView.setLayoutParams(lp);
textView.setOnClickListener(new FlexItemClickListener(viewIndex));
mFlexboxLayout.addView(textView);
}
});
}
FloatingActionButton removeFab = (FloatingActionButton) findViewById(
R.id.remove_fab);
if (removeFab != null) {
removeFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mFlexboxLayout.getChildCount() == 0) {
return;
}
mFlexboxLayout.removeViewAt(mFlexboxLayout.getChildCount() - 1);
}
});
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
ArrayList<FlexItem> flexItems = new ArrayList<>();
for (int i = 0; i < mFlexboxLayout.getChildCount(); i++) {
View child = mFlexboxLayout.getChildAt(i);
FlexboxLayout.LayoutParams lp = (FlexboxLayout.LayoutParams) child.getLayoutParams();
FlexItem flexItem = new FlexItem();
flexItem.index = i;
flexItem.order = lp.order;
flexItem.flexGrow = lp.flexGrow;
flexItem.flexShrink = lp.flexShrink;
flexItem.alignSelf = lp.alignSelf;
flexItem.minWidth = Util.pixelToDp(this, lp.width);
flexItem.minHeight = Util.pixelToDp(this, lp.height);
flexItem.topMargin = lp.topMargin;
flexItem.startMargin = lp.getMarginStart();
flexItem.endMargin = lp.getMarginEnd();
flexItem.bottomMargin = lp.bottomMargin;
flexItem.paddingTop = child.getPaddingTop();
flexItem.paddingStart = ViewCompat.getPaddingStart(child);
flexItem.paddingEnd = ViewCompat.getPaddingEnd(child);
flexItem.paddingBottom = child.getPaddingBottom();
flexItems.add(flexItem);
}
outState.putParcelableArrayList(FLEX_ITEMS_KEY, flexItems);
}
@Override
public boolean onNavigationItemSelected(MenuItem item) {
return false;
}
private TextView createBaseFlexItemTextView(int index) {
TextView textView = new TextView(this);
textView.setBackgroundResource(R.drawable.flex_item_background);
textView.setText(String.valueOf(index + 1));
textView.setGravity(Gravity.CENTER);
return textView;
}
private int getRandomFlexItemWidth(Resources resources, float defaultValue) {
SecureRandom random = new SecureRandom();
TypedArray candidates = resources.obtainTypedArray(R.array.flex_item_width_candidates);
int width = (int) candidates
.getDimension(random.nextInt(candidates.length()), defaultValue);
candidates.recycle();
return width;
}
private class FlexItemClickListener implements View.OnClickListener {
int mViewIndex;
FlexItemClickListener(int viewIndex) {
mViewIndex = viewIndex;
}
@Override
public void onClick(View v) {
// TODO: Implement the onClick listener
}
}
}
/*
* Copyright 2016 Google Inc. All rights reserved.
*
* 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.google.android.apps.flexbox;
import android.content.Context;
import android.util.DisplayMetrics;
/**
* Utilities class.
*/
public class Util {
/**
* Convert pixel to dp. Preserve the negative value as it's used for representing
* MATCH_PARENT(-1) and WRAP_CONTENT(-2).
* Ignore the round error that might happen in dividing the pixel by the density.
*
* @param context the context
* @param pixel the value in pixel
* @return the converted value in dp
*/
public static int pixelToDp(Context context, int pixel) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return pixel < 0 ? pixel : (int) (pixel / displayMetrics.density);
}
/**
* Convert dp to pixel. Preserve the negative value as it's used for representing
* MATCH_PARENT(-1) and WRAP_CONTENT(-2).
*
* @param context the context
* @param dp the value in dp
* @return the converted value in pixel
*/
public static int dpToPixel(Context context, int dp) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return dp < 0 ? dp : (int) (dp * displayMetrics.density);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册