[SetupWizardLib] Add support for require scrolling

Add SetupWizardLayout.requireScrollToBottom method that will register
a RequireScrollHelper on the layout. When the helper is registered
and the content view can scroll, the next button will be hidden and a
down-arrow button is shown, which will scroll the page down when
clicked.

Change-Id: Ib9ddcbeec24169cc00265fe107deb1b5099cba8d
This commit is contained in:
Maurice Lam 2015-05-28 19:36:19 -07:00
parent b5c78954c3
commit 2d77e072fe
22 changed files with 302 additions and 2 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
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.
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/suw_navbar_ic_down_arrow_dark" />

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
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.
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/suw_navbar_ic_down_arrow_light" />

View file

@ -86,6 +86,7 @@
<item name="suwNavBarBackButton">@drawable/suw_navbar_ic_back_dark</item>
<item name="suwNavBarBackgroundColor">@color/suw_navbar_bg_dark</item>
<item name="suwNavBarButtonBackground">@drawable/suw_navbar_btn_bg_dark</item>
<item name="suwNavBarMoreButton">@drawable/suw_navbar_ic_more_dark</item>
<item name="suwNavBarNextButton">@drawable/suw_navbar_ic_next_dark</item>
<item name="suwNavBarTextColor">@color/suw_navbar_text_dark</item>
</style>
@ -94,6 +95,7 @@
<item name="suwNavBarBackButton">@drawable/suw_navbar_ic_back_light</item>
<item name="suwNavBarBackgroundColor">@color/suw_navbar_bg_light</item>
<item name="suwNavBarButtonBackground">@drawable/suw_navbar_btn_bg_light</item>
<item name="suwNavBarMoreButton">@drawable/suw_navbar_ic_more_light</item>
<item name="suwNavBarNextButton">@drawable/suw_navbar_ic_next_light</item>
<item name="suwNavBarTextColor">@color/suw_navbar_text_light</item>
</style>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/suw_navbar_ic_intrinsic_size"
android:height="@dimen/suw_navbar_ic_intrinsic_size"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/suwNavBarTextColor"
android:pathData="M16.6,8.6l-4.6,4.6 -4.6,-4.6 -1.4,1.4 6,6 6,-6z"/>
</vector>

View file

@ -32,6 +32,17 @@
android:layout_weight="1"
android:visibility="invisible" />
<view class="com.android.setupwizardlib.view.NavigationBar$NavButton"
android:id="@+id/suw_navbar_more"
style="@style/SuwNavBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:contentDescription="@string/suw_more_button_label"
android:drawableEnd="?attr/suwNavBarMoreButton"
android:drawableRight="?attr/suwNavBarMoreButton"
android:gravity="end|center_vertical"
android:visibility="gone" />
<view class="com.android.setupwizardlib.view.NavigationBar$NavButton"
android:id="@+id/suw_navbar_next"
style="@style/SuwNavBarButtonStyle"

View file

@ -54,6 +54,7 @@
<item name="suwNavBarBackButton">@drawable/suw_navbar_ic_back</item>
<item name="suwNavBarBackgroundColor">@color/suw_navbar_bg_dark</item>
<item name="suwNavBarButtonBackground">@drawable/suw_navbar_btn_bg</item>
<item name="suwNavBarMoreButton">@drawable/suw_navbar_ic_more</item>
<item name="suwNavBarNextButton">@drawable/suw_navbar_ic_next</item>
<item name="suwNavBarTextColor">@color/suw_navbar_text_dark</item>
</style>
@ -62,6 +63,7 @@
<item name="suwNavBarBackButton">@drawable/suw_navbar_ic_back</item>
<item name="suwNavBarBackgroundColor">@color/suw_navbar_bg_light</item>
<item name="suwNavBarButtonBackground">@drawable/suw_navbar_btn_bg</item>
<item name="suwNavBarMoreButton">@drawable/suw_navbar_ic_more</item>
<item name="suwNavBarNextButton">@drawable/suw_navbar_ic_next</item>
<item name="suwNavBarTextColor">@color/suw_navbar_text_light</item>
</style>

View file

@ -23,6 +23,7 @@
<attr name="suwNavBarBackButton" format="reference" />
<attr name="suwNavBarBackgroundColor" format="color" />
<attr name="suwNavBarButtonBackground" format="color|reference" />
<attr name="suwNavBarMoreButton" format="reference" />
<attr name="suwNavBarNextButton" format="reference" />
<attr name="suwNavBarTextColor" format="color" />
<attr name="suwNavBarTheme" format="reference" />

View file

@ -16,9 +16,12 @@
-->
<resources>
<!-- Button for going to the next screen or step [CHAR LIMIT=40] -->
<!-- Button for going to the next screen or step [CHAR LIMIT=20] -->
<string name="suw_next_button_label">Next</string>
<!-- Button for going to the previous screen or step [CHAR LIMIT=40] -->
<!-- Button for going to the previous screen or step [CHAR LIMIT=20] -->
<string name="suw_back_button_label">Back</string>
<!-- Button for scrolling down to reveal more content on the screen [CHAR LIMIT=20] -->
<string name="suw_more_button_label">More</string>
</resources>

View file

@ -29,6 +29,7 @@ import android.os.Build.VERSION_CODES;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
@ -38,6 +39,8 @@ import android.view.ViewStub;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.setupwizardlib.util.RequireScrollHelper;
import com.android.setupwizardlib.view.BottomScrollView;
import com.android.setupwizardlib.view.Illustration;
import com.android.setupwizardlib.view.NavigationBar;
@ -222,6 +225,22 @@ public class SetupWizardLayout extends FrameLayout {
return view instanceof NavigationBar ? (NavigationBar) view : null;
}
private BottomScrollView getScrollView() {
final View view = findViewById(R.id.suw_bottom_scroll_view);
return view instanceof BottomScrollView ? (BottomScrollView) view : null;
}
public void requireScrollToBottom() {
final NavigationBar navigationBar = getNavigationBar();
final BottomScrollView scrollView = getScrollView();
if (navigationBar != null && scrollView != null) {
RequireScrollHelper.requireScroll(navigationBar, scrollView);
} else {
Log.e(TAG, "Both suw_layout_navigation_bar and suw_bottom_scroll_view must exist in"
+ " the template to require scrolling.");
}
}
public void setHeaderText(int title) {
final TextView titleView = (TextView) findViewById(R.id.suw_layout_title);
if (titleView != null) {

View file

@ -0,0 +1,86 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.android.setupwizardlib.util;
import android.view.View;
import android.widget.ScrollView;
import com.android.setupwizardlib.view.BottomScrollView;
import com.android.setupwizardlib.view.NavigationBar;
/**
* Add this helper to require the scroll view to be scrolled to the bottom, making sure that the
* user sees all content on the screen. This will change the navigation bar to show the more button
* instead of the next button when there is more content to be seen. When the more button is
* clicked, the scroll view will be scrolled one page down.
*/
public class RequireScrollHelper implements BottomScrollView.BottomScrollListener,
View.OnClickListener {
/**
* Require scrolling on the scrollView, so that if the scrollView has content hidden beneath the
* fold, the next button will be hidden and the more button will be shown instead. The more
* button will scroll the scrollView downwards when clicked until the bottom is reached.
*
* @param navigationBar The navigation bar in which the next button's label will be changed.
* @param scrollView The {@link BottomScrollView} to be scrolled.
*/
public static RequireScrollHelper requireScroll(NavigationBar navigationBar,
BottomScrollView scrollView) {
final RequireScrollHelper helper = new RequireScrollHelper(navigationBar, scrollView);
helper.requireScroll();
return helper;
}
private final BottomScrollView mScrollView;
private final NavigationBar mNavigationBar;
private boolean mScrollNeeded;
private RequireScrollHelper(NavigationBar navigationBar, BottomScrollView scrollView) {
mNavigationBar = navigationBar;
mScrollView = scrollView;
}
private void requireScroll() {
mNavigationBar.getMoreButton().setOnClickListener(this);
mScrollView.setBottomScrollListener(this);
}
@Override
public void onScrolledToBottom() {
if (mScrollNeeded) {
mNavigationBar.getNextButton().setVisibility(View.VISIBLE);
mNavigationBar.getMoreButton().setVisibility(View.GONE);
mScrollNeeded = false;
}
}
@Override
public void onRequiresScroll() {
if (!mScrollNeeded) {
mNavigationBar.getNextButton().setVisibility(View.GONE);
mNavigationBar.getMoreButton().setVisibility(View.VISIBLE);
mScrollNeeded = true;
}
}
@Override
public void onClick(View view) {
mScrollView.pageScroll(ScrollView.FOCUS_DOWN);
}
}

View file

@ -68,6 +68,7 @@ public class NavigationBar extends LinearLayout implements View.OnClickListener
private Button mNextButton;
private Button mBackButton;
private Button mMoreButton;
private NavigationBarListener mListener;
public NavigationBar(Context context) {
@ -92,6 +93,7 @@ public class NavigationBar extends LinearLayout implements View.OnClickListener
View.inflate(getContext(), R.layout.suw_navbar_view, this);
mNextButton = (Button) findViewById(R.id.suw_navbar_next);
mBackButton = (Button) findViewById(R.id.suw_navbar_back);
mMoreButton = (Button) findViewById(R.id.suw_navbar_more);
}
public Button getBackButton() {
@ -102,6 +104,10 @@ public class NavigationBar extends LinearLayout implements View.OnClickListener
return mNextButton;
}
public Button getMoreButton() {
return mMoreButton;
}
public void setNavigationBarListener(NavigationBarListener listener) {
mListener = listener;
if (mListener != null) {

View file

@ -0,0 +1,104 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.android.setupwizardlib.test;
import android.content.Context;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.View;
import com.android.setupwizardlib.util.RequireScrollHelper;
import com.android.setupwizardlib.view.BottomScrollView;
import com.android.setupwizardlib.view.NavigationBar;
public class RequireScrollHelperTest extends AndroidTestCase {
private TestBottomScrollView mScrollView;
private NavigationBar mNavigationBar;
@Override
protected void setUp() throws Exception {
super.setUp();
mScrollView = new TestBottomScrollView(getContext());
mNavigationBar = new NavigationBar(getContext());
}
@SmallTest
public void testRequireScroll() {
RequireScrollHelper.requireScroll(mNavigationBar, mScrollView);
assertEquals("More button should be gone initially", View.GONE,
mNavigationBar.getMoreButton().getVisibility());
assertEquals("Next button should be shown", View.VISIBLE,
mNavigationBar.getNextButton().getVisibility());
mScrollView.listener.onRequiresScroll();
assertEquals("More button should be shown when scroll is required", View.VISIBLE,
mNavigationBar.getMoreButton().getVisibility());
assertEquals("Next button should not be shown when scroll is required", View.GONE,
mNavigationBar.getNextButton().getVisibility());
}
@SmallTest
public void testScrolledToBottom() {
RequireScrollHelper.requireScroll(mNavigationBar, mScrollView);
mScrollView.listener.onRequiresScroll();
assertEquals("More button should be shown when scroll is required", View.VISIBLE,
mNavigationBar.getMoreButton().getVisibility());
assertEquals("Next button should not be shown when scroll is required", View.GONE,
mNavigationBar.getNextButton().getVisibility());
mScrollView.listener.onScrolledToBottom();
assertEquals("More button should be hidden when scrolled to bottom", View.GONE,
mNavigationBar.getMoreButton().getVisibility());
assertEquals("Next button should be shown when scrolled to bottom", View.VISIBLE,
mNavigationBar.getNextButton().getVisibility());
}
@SmallTest
public void testClickScrollButton() {
RequireScrollHelper.requireScroll(mNavigationBar, mScrollView);
assertEquals("ScrollView page should be initially 0", 0, mScrollView.page);
mScrollView.listener.onRequiresScroll();
mNavigationBar.getMoreButton().performClick();
assertEquals("ScrollView page should be scrolled by 1", 1, mScrollView.page);
}
private static class TestBottomScrollView extends BottomScrollView {
public BottomScrollListener listener;
public int page = 0;
public TestBottomScrollView(Context context) {
super(context);
}
@Override
public void setBottomScrollListener(BottomScrollListener listener) {
this.listener = listener;
}
@Override
public boolean pageScroll(int direction) {
if (direction == FOCUS_DOWN) {
page++;
} else if (direction == FOCUS_UP) {
page--;
}
return super.pageScroll(direction);
}
}
}