Skip to content

Commit

Permalink
Merge pull request #88 from hidroh/list
Browse files Browse the repository at this point in the history
Allow scroll to top action
  • Loading branch information
hidroh committed Oct 7, 2015
2 parents 534bc19 + 13a2d60 commit b33a51d
Show file tree
Hide file tree
Showing 13 changed files with 228 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,19 @@ protected void onCreate(Bundle savedInstanceState) {
mContentView = (CoordinatorLayout) findViewById(R.id.content_frame);
mViewPager = (ViewPager) findViewById(R.id.content);
onCreateView();
final Fragment fragment = instantiateListFragment();
if (fragment instanceof Scrollable) {
findViewById(R.id.toolbar).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((Scrollable) fragment).scrollToTop();
}
});
}
getSupportFragmentManager()
.beginTransaction()
.replace(android.R.id.list,
instantiateListFragment(),
fragment,
LIST_FRAGMENT_TAG)
.commit();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
import io.github.hidroh.materialistic.widget.ListRecyclerViewAdapter;

public class FavoriteFragment extends BaseFragment
implements LoaderManager.LoaderCallbacks<Cursor> {
implements LoaderManager.LoaderCallbacks<Cursor>, Scrollable {
private RecyclerView mRecyclerView;
private FavoriteManager.Cursor mCursor;
private RecyclerViewAdapter mAdapter;
private BroadcastReceiver mBroadcastReceiver;
Expand Down Expand Up @@ -141,15 +142,15 @@ public void onDestroyActionMode(ActionMode actionMode) {
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
final View view = getLayoutInflater(savedInstanceState).inflate(R.layout.fragment_favorite, container, false);
mAdapter = new RecyclerViewAdapter();
final RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()) {
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()) {
@Override
public int getOrientation() {
return LinearLayout.VERTICAL;
}
});
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(mAdapter);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setAdapter(mAdapter);
mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
Expand Down Expand Up @@ -312,6 +313,11 @@ public void onDetach() {
super.onDetach();
}

@Override
public void scrollToTop() {
mRecyclerView.smoothScrollToPosition(0);
}

/**
* Filters list data by given query
* @param query query used to filter data
Expand Down
46 changes: 38 additions & 8 deletions app/src/main/java/io/github/hidroh/materialistic/ItemActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import io.github.hidroh.materialistic.data.HackerNewsClient;
import io.github.hidroh.materialistic.data.ItemManager;

public class ItemActivity extends BaseItemActivity {
public class ItemActivity extends BaseItemActivity implements Scrollable {

public static final String EXTRA_ITEM = ItemActivity.class.getName() + ".EXTRA_ITEM";
public static final String EXTRA_ITEM_ID = ItemActivity.class.getName() + ".EXTRA_ITEM_ID";
Expand All @@ -44,6 +44,7 @@ public class ItemActivity extends BaseItemActivity {
@Inject FavoriteManager mFavoriteManager;
@Inject AlertDialogBuilder mAlertDialogBuilder;
private TabLayout mTabLayout;
private AppBarLayout mAppBar;

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand All @@ -54,6 +55,7 @@ protected void onCreate(Bundle savedInstanceState) {
getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME |
ActionBar.DISPLAY_HOME_AS_UP);
mBookmark = (ImageView) findViewById(R.id.bookmarked);
mAppBar = (AppBarLayout) findViewById(R.id.appbar);
mTabLayout = (TabLayout) findViewById(R.id.tab_layout);
mTabLayout.setTabMode(TabLayout.MODE_FIXED);
mTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
Expand Down Expand Up @@ -163,6 +165,11 @@ protected void onPause() {
super.onPause();
}

@Override
public void scrollToTop() {
mAppBar.setExpanded(true, true);
}

private void bindFavorite() {
if (mItem == null) {
return;
Expand Down Expand Up @@ -247,17 +254,16 @@ private void bindData(final ItemManager.Item story) {
R.drawable.ic_poll_grey600_18dp, 0, 0, 0);
break;
}
ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
final Bundle args = new Bundle();
final Fragment[] fragments = new Fragment[2];
Bundle args = new Bundle();
args.putInt(EXTRA_ITEM_LEVEL, getIntent().getIntExtra(EXTRA_ITEM_LEVEL, 0));
fragments[0] = ItemFragment.instantiate(ItemActivity.this, story, args);
fragments[1] = WebFragment.instantiate(ItemActivity.this, story);
final ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
if (position == 0) {
return ItemFragment.instantiate(ItemActivity.this, mItem, args);
} else {
return WebFragment.instantiate(ItemActivity.this, mItem);
}
return fragments[position];
}

@Override
Expand All @@ -275,6 +281,30 @@ public CharSequence getPageTitle(int position) {
}
});
mTabLayout.setupWithViewPager(viewPager);
mTabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
private boolean mSelected = false;

@Override
public void onTabReselected(TabLayout.Tab tab) {
if (mSelected) {
return;
}
((Scrollable) fragments[viewPager.getCurrentItem()]).scrollToTop();
scrollToTop();
}

@Override
public void onTabSelected(TabLayout.Tab tab) {
mSelected = true; // TODO https://code.google.com/p/android/issues/detail?id=177189
viewPager.setCurrentItem(tab.getPosition());
mSelected = false;
}

@Override
public void onTabUnselected(TabLayout.Tab tab) {
// no-op
}
});
if (viewPager.getAdapter().getCount() < 2) {
AppBarLayout.LayoutParams p = (AppBarLayout.LayoutParams) mTabLayout.getLayoutParams();
p.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import io.github.hidroh.materialistic.widget.MultiPageItemRecyclerViewAdapter;
import io.github.hidroh.materialistic.widget.SinglePageItemRecyclerViewAdapter;

public class ItemFragment extends BaseFragment {
public class ItemFragment extends BaseFragment implements Scrollable {

private static final String EXTRA_ITEM = ItemFragment.class.getName() + ".EXTRA_ITEM";
private RecyclerView mRecyclerView;
Expand Down Expand Up @@ -116,6 +116,11 @@ public void onPause() {
super.onPause();
}

@Override
public void scrollToTop() {
mRecyclerView.smoothScrollToPosition(0);
}

private void loadKidData() {
mItemManager.getItem(mItemId, new ItemManager.ResponseListener<ItemManager.Item>() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
import io.github.hidroh.materialistic.data.SessionManager;
import io.github.hidroh.materialistic.widget.ListRecyclerViewAdapter;

public class ListFragment extends BaseFragment {
public class ListFragment extends BaseFragment implements Scrollable {

private static final String EXTRA_ITEMS = ListFragment.class.getName() + ".EXTRA_ITEMS";
private RecyclerView mRecyclerView;
Expand Down Expand Up @@ -202,6 +202,11 @@ public void onDetach() {
super.onDetach();
}

@Override
public void scrollToTop() {
mRecyclerView.smoothScrollToPosition(0);
}

private void bindData() {
mItemManager.getStories(mFilter, new ItemManager.ResponseListener<ItemManager.Item[]>() {
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.github.hidroh.materialistic;

public interface Scrollable {
void scrollToTop();
}
14 changes: 12 additions & 2 deletions app/src/main/java/io/github/hidroh/materialistic/WebFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.widget.NestedScrollView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
Expand All @@ -27,11 +28,12 @@

import io.github.hidroh.materialistic.data.ItemManager;

public class WebFragment extends BaseFragment {
public class WebFragment extends BaseFragment implements Scrollable {

private static final String EXTRA_ITEM = WebFragment.class.getName() + ".EXTRA_ITEM";
private ItemManager.WebItem mItem;
private WebView mWebView;
private NestedScrollView mScrollView;
private boolean mIsHackerNewsUrl;
private Intent mDownloadIntent;
@Inject @Named(ActivityModule.HN) ItemManager mItemManager;
Expand All @@ -51,6 +53,7 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
}

final View view = getLayoutInflater(savedInstanceState).inflate(R.layout.fragment_web, container, false);
mScrollView = (NestedScrollView) view.findViewById(R.id.nested_scroll_view);
final ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.progress);
mWebView = (WebView) view.findViewById(R.id.web_view);
mWebView.setBackgroundColor(Color.TRANSPARENT);
Expand Down Expand Up @@ -102,7 +105,9 @@ public boolean onKey(View v, int keyCode, KeyEvent event) {
private View createLocalView(ViewGroup container, Bundle savedInstanceState) {
final View view = getLayoutInflater(savedInstanceState)
.inflate(R.layout.fragment_web_hn, container, false);
((TextView) view.findViewById(R.id.posted)).setText(mItem.getDisplayedTime(getActivity(), false));
mScrollView = (NestedScrollView) view.findViewById(R.id.nested_scroll_view);
((TextView) view.findViewById(R.id.posted))
.setText(mItem.getDisplayedTime(getActivity(), false));
if (mItem.getType().equals(ItemManager.Item.COMMENT_TYPE)) {
view.findViewById(R.id.title).setVisibility(View.GONE);
} else {
Expand Down Expand Up @@ -190,4 +195,9 @@ private void setWebViewSettings(WebSettings webSettings) {
webSettings.setDisplayZoomControls(false);
}
}

@Override
public void scrollToTop() {
mScrollView.smoothScrollTo(0, 0);
}
}
1 change: 1 addition & 0 deletions app/src/main/res/layout/fragment_web.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/nested_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">

Expand Down
68 changes: 37 additions & 31 deletions app/src/main/res/layout/fragment_web_hn.xml
Original file line number Diff line number Diff line change
@@ -1,39 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/nested_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin">
<TextView
android:id="@id/posted"
android:text="@string/loading_text"
android:drawablePadding="@dimen/padding"
android:singleLine="false"
style="@style/textSubtitleStyle.Italic" />
<TextView
android:id="@id/title"
android:paddingLeft="@dimen/padding"
android:paddingRight="@dimen/padding"
android:text="@string/loading_text"
style="@style/textTitleStyle"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:scrollbars="none">
android:layout_height="match_parent"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin">
<TextView
android:id="@id/posted"
android:text="@string/loading_text"
android:drawablePadding="@dimen/padding"
android:singleLine="false"
style="@style/textSubtitleStyle.Italic" />
<TextView
android:id="@id/text"
android:id="@id/title"
android:paddingLeft="@dimen/padding"
android:paddingRight="@dimen/padding"
android:text="@string/loading_text"
style="@style/textContentStyle"
style="@style/textTitleStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ScrollView>
</LinearLayout>
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:scrollbars="none">
<TextView
android:id="@id/text"
android:paddingLeft="@dimen/padding"
android:paddingRight="@dimen/padding"
android:text="@string/loading_text"
style="@style/textContentStyle"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ScrollView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.content.Intent;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.text.format.DateUtils;

import org.junit.After;
Expand All @@ -12,11 +13,13 @@
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.internal.ShadowExtractor;
import org.robolectric.res.builder.RobolectricPackageManager;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowResolveInfo;
import org.robolectric.util.ActivityController;

import io.github.hidroh.materialistic.test.ShadowRecyclerView;
import io.github.hidroh.materialistic.test.ShadowSupportPreferenceManager;
import io.github.hidroh.materialistic.test.TestListActivity;
import io.github.hidroh.materialistic.test.TestWebItem;
Expand All @@ -31,7 +34,7 @@
import static org.robolectric.Shadows.shadowOf;

// TODO switch to API 21 once ShareActionProvider is fixed
@Config(sdk = 19, shadows = {ShadowSupportPreferenceManager.class})
@Config(sdk = 19, shadows = {ShadowSupportPreferenceManager.class, ShadowRecyclerView.class})
@RunWith(RobolectricGradleTestRunner.class)
public class BaseListActivityTest {
private ActivityController<TestListActivity> controller;
Expand Down Expand Up @@ -147,6 +150,17 @@ public void testLastUpdated() {
assertThat(activity.getSupportActionBar()).hasSubtitle(expected);
}

@Test
public void testScrollToTop() {
RecyclerView recyclerView = (RecyclerView) activity.findViewById(R.id.recycler_view);
recyclerView.smoothScrollToPosition(1);
assertEquals(1, ((ShadowRecyclerView) ShadowExtractor.extract(recyclerView))
.getSmoothScrollToPosition());
activity.findViewById(R.id.toolbar).performClick();
assertEquals(0, ((ShadowRecyclerView) ShadowExtractor.extract(recyclerView))
.getSmoothScrollToPosition());
}

@After
public void tearDown() {
controller.pause().stop().destroy();
Expand Down
Loading

0 comments on commit b33a51d

Please sign in to comment.