Swiperefreshlayout not refreshing for layouts - android

swiperefreshlayout not working with diffrent view. I have created main layout and included a layout in it as shown in code.
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_refresh_layout_home"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/toolbar">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/toolbar">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_post_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</android.support.v7.widget.RecyclerView>
<include layout="#layout/layout_util" />
</RelativeLayout>
</android.support.v4.widget.SwipeRefreshLayout>

SwipeRefreshLayout only supports a single ListView or GridView child. Official src
So you can try SwipeRefreshMultipleViews
Edited:
First create on Custom Class for MultiSwipeRefreshLayout
public class MultiSwipeRefreshLayout extends SwipeRefreshLayout {
private View[] mSwipeableChildren;
public MultiSwipeRefreshLayout(Context context) {
super(context);
}
public MultiSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Set the children which can trigger a refresh by swiping down when they are visible. These
* views need to be a descendant of this view.
*/
public void setSwipeableChildren(final int... ids) {
assert ids != null;
// Iterate through the ids and find the Views
mSwipeableChildren = new View[ids.length];
for (int i = 0; i < ids.length; i++) {
mSwipeableChildren[i] = findViewById(ids[i]);
}
}
// BEGIN_INCLUDE(can_child_scroll_up)
/**
* This method controls when the swipe-to-refresh gesture is triggered. By returning false here
* we are signifying that the view is in a state where a refresh gesture can start.
* <p>
* <p>As {#link SwipeRefreshLayout} only supports one direct child by
* default, we need to manually iterate through our swipeable children to see if any are in a
* state to trigger the gesture. If so we return false to start the gesture.
*/
#Override
public boolean canChildScrollUp() {
if (mSwipeableChildren != null && mSwipeableChildren.length > 0) {
// Iterate through the scrollable children and check if any of them can not scroll up
for (View view : mSwipeableChildren) {
if (view != null && view.isShown() && !canViewScrollUp(view)) {
// If the view is shown, and can not scroll upwards, return false and start the
// gesture.
return false;
}
}
}
return true;
}
// END_INCLUDE(can_child_scroll_up)
// BEGIN_INCLUDE(can_view_scroll_up)
/**
* Utility method to check whether a {#link View} can scroll up from it's current position.
* Handles platform version differences, providing backwards compatible functionality where
* needed.
*/
private static boolean canViewScrollUp(View view) {
if (android.os.Build.VERSION.SDK_INT >= 14) {
// For ICS and above we can call canScrollVertically() to determine this
return ViewCompat.canScrollVertically(view, -1);
} else {
if (view instanceof AbsListView) {
// Pre-ICS we need to manually check the first visible item and the child view's top
// value
final AbsListView listView = (AbsListView) view;
return listView.getChildCount() > 0 &&
(listView.getFirstVisiblePosition() > 0
|| listView.getChildAt(0).getTop() < listView.getPaddingTop());
} else {
// For all other view types we just check the getScrollY() value
return view.getScrollY() > 0;
}
}
}
// END_INCLUDE(can_view_scroll_up)
}
then change your layout like this,
<?xml version="1.0" encoding="utf-8"?>
<in.muthu.stackoverflow.ui.widget.MultiSwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/swiperefresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<GridView
android:id="#+id/contact_screen_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="2" />
<TextView
android:id="#android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/empty_text"
android:layout_gravity="center"/>
</FrameLayout>
</FrameLayout>
</in.muthu.stackoverflow.ui.widget.MultiSwipeRefreshLayout>
Note: change in.muthu.stackoverflow.ui.widget with your
MultiSwipeRefreshLayout with your package
And in your java code you should do this,
mSwipeRefreshLayout = (MultiSwipeRefreshLayout) view.findViewById(R.id.swiperefresh);
mSwipeRefreshLayout.setSwipeableChildren(R.id.contact_screen_list);

Related

Android GridView cell sequence changing

I'm getting a very odd (to me) issue when populating a GridView - when it first loads all looks fine, but scrolling ends up with data from cells moving to other positions. In the following example I just have an ArrayList of the numbers 0-800, and an 8 column grid. When loaded 0-7 is in the first row, 8-15 in the second etc, with a different value in the first cell in each row. But after scrolling the first column value will change. Here's the code - it's driving me crazy!
MainActivity.java:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<Integer> positions = new ArrayList<Integer>();
for(int i=0;i<800;i++) {
positions.add(i);
}
GridView gvVals = findViewById(R.id.gvVals);
CellAdapter adapter = new CellAdapter(this, new ArrayList<Integer>());
adapter = new CellAdapter(this, positions);
gvVals.setAdapter(adapter);
}
Cell Adapter.java:
public class CellAdapter extends ArrayAdapter<Integer> {
private Context context;
public CellAdapter(Context context, ArrayList<Integer> vals) {
super(context, 0, vals);
this.context = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null) {
v = LayoutInflater.from(getContext()).inflate(R.layout.activity_cell, parent, false);
} else {
v = (View)convertView;
}
TextView tvDay = v.findViewById(R.id.tvVal);
tvDay.setVisibility(View.VISIBLE);
if(position % 8 == 0) {
tvDay.setText("ST:"+position);
}
else
{
v.findViewById(R.id.btnM).setVisibility(View.VISIBLE);
((Button)v.findViewById(R.id.btnM)).setText(position + " ");
tvDay.setText(" ");
}
return v;
}
ActivityMain.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<GridView
android:id="#+id/gvVals"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="60dp"
android:numColumns="8"/>
</android.support.constraint.ConstraintLayout>
ActivityCell.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/tvVal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:visibility="invisible"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:id="#+id/btnM"
android:layout_width="0dp"
android:layout_weight=".34"
android:layout_height="20dp"
android:text="M"
android:background="#color/colorAccent"
android:minWidth="0dp"
android:visibility="invisible"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
GridView when app first opened
After scrolling down a few rows and then back up
Change this:
TextView tvDay = v.findViewById(R.id.tvVal);
tvDay.setVisibility(View.VISIBLE);
if(position % 8 == 0) {
tvDay.setText("ST:"+position);
}
else
{
v.findViewById(R.id.btnM).setVisibility(View.VISIBLE);
((Button)v.findViewById(R.id.btnM)).setText(position + " ");
tvDay.setText(" ");
}
to this:
boolean firstColumn = position % 8 == 0;
TextView tvDay = v.findViewById(R.id.tvVal);
tvDay.setVisibility(firstColumn ? View.VISIBLE : View.GONE);
Button btnM = v.findViewById(R.id.btnM);
btnM.setVisibility(firstColumn ? View.GONE : View.VISIBLE);
if(firstColumn) {
tvDay.setText("ST:"+position);
}
else
{
btnM.setText(position + " ");
}
It's important when working with RecyclerView to make sure that you always update every view every time. This is because old view holders get recycled, so if you only do something some of the time, it can get overwritten or it can overwrite the "expected" behavior.
Previously, you were making btnM visible if it wasn't the first column, but you weren't making it not visible if it was the first column.

Dynamicly sized preference fragment with in drawer

I am currently trying to use a preference fragment within a Navigation Drawer. There are multiple preference fragments that may populate a frame layout, all of which could be of a different size and may not be the full height of the parent.
For this reason I was wanting to use wrap_content for the frame layout height, but as preference fragment extends from ListView this causes problems (reference ListView Wrap Content). wrap_content does provide the desired result, although I can see that OnBindView is being called continuously, which highly inefficient and causes my data binding methods to be called all the time.
Has anyone got any simple out the box solutions I could try? Or would this be a case for a custom View where I would measure the children of the fragment and set the height at run time? Any help on this would be much appreciated.
The layout below shows the drawer layout which is included in the main layout.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:orientation="horizontal"
android:padding="20dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RadioGroup
android:id="#+id/drawer_radio"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RadioButton
android:id="#+id/drawer_radio_button_0"
style="#style/ButtonDrawer"
android:background="#drawable/fft_button"
android:button="#null"
android:contentDescription="#string/image_button"/>
<RadioButton
android:id="#+id/drawer_radio_button_1"
style="#style/ButtonDrawer"
android:background="#drawable/trigger_button"
android:button="#null"
android:contentDescription="#string/image_button"/>
</RadioGroup>
</LinearLayout>
<FrameLayout
android:id="#+id/drawer_preference_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
tools:layout="#android:layout/simple_list_item_1"
android:background="#drawable/floating_preference_background_shape"/>
</LinearLayout>
Ok, so I think I found a good solution so I thought I'd post it for others to use.
The way I had to do this was to extend from PreferenceFragment and do some measurements at run time, in which I can use to resize the listView.
I did this with the following.
public class PreferenceFragmentHeightWrap extends PreferenceFragment {
/**
* We can guarantee that all children will be in the adaptor list by this point
*/
#Override
public void onResume() {
super.onResume();
setListViewHeightBasedOnItems(getView());
}
/**
* Sets ListView height dynamically based on the height of the items.
*
* #return true if the listView is successfully resized, false otherwise
*/
public boolean setListViewHeightBasedOnItems(View view) {
ListView listView = (ListView) view.findViewById(android.R.id.list);
ListAdapter listAdapter = listView.getAdapter();
if(listAdapter != null) {
int numberOfItems = listAdapter.getCount();
// Get total height of all items.
int totalItemsHeight = 0;
for(int itemPos = 0; itemPos < numberOfItems; itemPos++) {
View item = listAdapter.getView(itemPos, null, listView);
item.measure(0, 0);
totalItemsHeight += item.getMeasuredHeight();
}
// Get total height of all item dividers.
int totalDividersHeight = listView.getDividerHeight() *
(numberOfItems - 1);
// Set list height.
ViewGroup.LayoutParams params = view.getLayoutParams();
params.height = totalItemsHeight + totalDividersHeight + listView.getPaddingBottom()
+ listView.getPaddingTop();
view.setLayoutParams(params);
return true;
} else {
return false;
}
}
Hopefully this will make sense and feel free to comment if you see anything untoward.

Android: How do I add a thin grey horizontal line on alert-dialog before the buttons?

I'm using android.app.AlertDialog that contains a ScrollView and inside (of course) some content.
Google shows in its material-guidelines a small grey line above the buttons when the content is larger than the visible space: http://www.google.com/design/spec/components/dialogs.html#dialogs-behavior
My alert-dialog doesn't have this grey line. How do I create this line?
I already tried a background for the ScrollView like this:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="1dp"
android:color="#color/dark_transparent"/>
</shape>
But this created a line on top AND bottom. And it also appears when the content is smaller than the visible space, which looks ugly.
I found a solution for the grey line! :)
I found the solution how to show the grey line at all here: How to make a static button under a ScrollView?
For the check if I want to show it, I found the solution here: How can you tell when a layout has been drawn?
This is how my code looks like now:
This is my_material_dialog.xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ScrollView
android:id="#+id/myMaterialDialog_scrollView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fillViewport="true">
<LinearLayout
android:id="#+id/myMaterialDialog_textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="5dp"
android:paddingLeft="26dp"
android:paddingRight="26dp"
android:paddingTop="15dp">
<!-- dynamically added content goes here -->
</LinearLayout>
</ScrollView>
<View
android:id="#+id/myMaterialDialog_lineView"
android:layout_width="fill_parent"
android:layout_height="1dp"
android:layout_gravity="center_horizontal"
android:background="#15000000"
android:gravity="center_horizontal"
android:visibility="gone"/>
</LinearLayout>
And this is MyMaterialDialog.java:
public class MyMaterialDialog extends AlertDialog {
private Context context;
private ScrollView scrollView;
private LinearLayout textView;
private View lineView;
private boolean checkingLayout;
public MyMaterialDialog(final Context context) {
super(context);
this.context = context;
final View myMaterialDialog = getLayoutInflater().inflate(R.layout.my_material_dialog, null);
this.scrollView = (ScrollView) myMaterialDialog.findViewById(R.id.myMaterialDialog_scrollView);
this.textView = (LinearLayout) myMaterialDialog.findViewById(R.id.myMaterialDialog_textView);
this.lineView = myMaterialDialog.findViewById(R.id.myMaterialDialog_lineView);
final ViewTreeObserver vto = scrollView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (checkingLayout) {
// avoid infinite recursions
return;
}
checkingLayout = true;
if (scrollView.canScrollVertically(1)) {
lineView.setVisibility(View.VISIBLE);
} else {
lineView.setVisibility(View.GONE);
}
checkingLayout = false;
}
});
setTitle(R.string.myMaterialDialog_title);
setText();
setView(myMaterialDialog);
show();
}
/**
* do request to webserver for texts
*/
private final void setText() {
final GetDialogTextRequest request = new GetDialogTextRequest();
final GetDialogTextResultHandler resultHandler = new GetDialogTextResultHandler(context, textView);
request.submit(resultHandler);
}
}
private final class GetDialogTextResultHandler extends DefaultRequestResultHandler<List<MyTextObject>> {
private final Context context;
private final LinearLayout textView;
private GetDialogTextResultHandler(final Context context, final LinearLayout textView) {
super(context);
this.context = context;
this.textView = textView;
}
#Override
public void handleResult(final List<MyTextObject> texts) {
setText(texts); // ... sets the content, can vary in size
}
}
Add something like this below your ScrollView:
<View android:layout_width="fill_parent"
android:layout_height="2px"
android:background="#90909090"/>
It should give you a slim greyish horizontal bar.
If you're using API 23+ (Android 6.0) using the following in scroll view will add the top and bottom indicators.
android:scrollIndicators="top|bottom"
If targeting older API's I looked into Google's Alert Dialog controller source code, and am using the following code:
private static void setScrollIndicators(ViewGroup root, final NestedScrollView content,
final int indicators, final int mask) {
// use it like this:
// setScrollIndicators(contentPanel, content, indicators,
// ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_BOTTOM);
// Set up scroll indicators (if present).
View indicatorUp = root.findViewById(R.id.scrollIndicatorUp);
View indicatorDown = root.findViewById(R.id.scrollIndicatorDown);
if (Build.VERSION.SDK_INT >= 23) {
// We're on Marshmallow so can rely on the View APIsaa
ViewCompat.setScrollIndicators(content, indicators, mask);
// We can also remove the compat indicator views
if (indicatorUp != null) {
root.removeView(indicatorUp);
}
if (indicatorDown != null) {
root.removeView(indicatorDown);
}
} else {
// First, remove the indicator views if we're not set to use them
if (indicatorUp != null && (indicators & ViewCompat.SCROLL_INDICATOR_TOP) == 0) {
root.removeView(indicatorUp);
indicatorUp = null;
}
if (indicatorDown != null && (indicators & ViewCompat.SCROLL_INDICATOR_BOTTOM) == 0) {
root.removeView(indicatorDown);
indicatorDown = null;
}
if (indicatorUp != null || indicatorDown != null) {
final View top = indicatorUp;
final View bottom = indicatorDown;
if (content != null) {
// We're just showing the ScrollView, set up listener.
content.setOnScrollChangeListener(
new NestedScrollView.OnScrollChangeListener() {
#Override
public void onScrollChange(NestedScrollView v, int scrollX,
int scrollY,
int oldScrollX, int oldScrollY) {
manageScrollIndicators(v, top, bottom);
}
});
// Set up the indicators following layout.
content.post(new Runnable() {
#Override
public void run() {
manageScrollIndicators(content, top, bottom);
}
});
} else {
// We don't have any content to scroll, remove the indicators.
if (top != null) {
root.removeView(top);
}
if (bottom != null) {
root.removeView(bottom);
}
}
}
}
}
private static void manageScrollIndicators(View v, View upIndicator, View downIndicator) {
if (upIndicator != null) {
upIndicator.setVisibility(
ViewCompat.canScrollVertically(v, -1) ? View.VISIBLE : View.INVISIBLE);
}
if (downIndicator != null) {
downIndicator.setVisibility(
ViewCompat.canScrollVertically(v, 1) ? View.VISIBLE : View.INVISIBLE);
}
}
And XML looks like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:id="#+id/scrollIndicatorUp"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/dim_white"
android:visibility="gone"
tools:visibility="visible" />
<android.support.v4.widget.NestedScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<... you content here>
</android.support.v4.widget.NestedScrollView>
<View
android:id="#+id/scrollIndicatorDown"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/dim_white"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>

android Passing touch events of a Child view drawn outside of view clip bounds (ViewPager Page 1 to Page 2)

My Android ViewPager has set to draw views outside of the bounds (Clip bounds set to false) . I have a touch event listener on all the views displayed on the page. The custom view is displayed on the page 1, drawn outside of its clipping Bounds and overflows to the page 2. The touch event on the page 1 works fine. When scrolled to the second page, the remaining view is displayed. The issue is that the touch event on the custom view (added on the page 1) does not get called when clicked on the Page 2.
PageViewActivity.cs
ViewPager mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setClipChildren(false);
mViewPager.setClipToPadding(false);
PageViewFragment.cs
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.page_layout, container, false);
setHasOptionsMenu(true);
View view1=(View)v.findViewById(R.id.view1);
view1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getActivity(), "View clicked",Toast.LENGTH_SHORT).show();
}
});return v;
}
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false" >
<View
android:id="#+id/view1"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginLeft="250dp"
android:layout_marginTop="20dp"
android:background="#ff0000" />
Fragment Layout - page_layout.xml
Any suggestions?
You have to manually detect the touch events, and see if it's coordinates lie within the bounds of the pages outside the bounds. So going off of what Tatarize states, before dispatching touch events to the pager, you should check if the event is within one of the pages of the view pager.
Here is example code, I'm only checking if the x lies within the bounds and not the y in childContains().
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
return pager.dispatchTouchEvent(event);
case MotionEvent.ACTION_UP:
int childCount = pager.getChildCount();
int x = (int) event.getX();
int y = (int) event.getY();
for (int i = 0; i < childCount; i++) {
final View child = pager.getChildAt(i);
if (childContains(child, x)) {
// delay the click minimally in-case
// callOnClick performs mods on the pager
//
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
child.callOnClick();
}
}, 100);
return true;
}
}
break;
}
return pager.dispatchTouchEvent(event);
}
int[] loc = new int[2];
private boolean childContains(View child, final int x) {
child.getLocationOnScreen(loc);
int x1_child = loc[0];
int x2_child = x1_child + child.getWidth();
int x0 = x;
boolean isInside = x0 >= x1_child && x0 < x2_child;
Log.v("childContains()", String.format("x: %s x1_child: %s x2_child: %s isInside: %s",
x0, x1_child, x2_child, String.valueOf(isInside)));
return isInside;
}
There’s a problem here. We can swipe only the middle view. This is because the touch events occur outside the ViewPagers bounds. We can override the onTouchEvent of the ViewPagerContainer and dispatch the events to the ViewPager to solve this.
private ViewPager mPager;
#Override
protected void onFinishInflate() {
mPager = (ViewPager) getChildAt(0);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
return mPager.dispatchTouchEvent(ev);
}
But the truth is you're better off using the more modern solutions to this stuff.
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.TabLayout
android:id="#+id/result_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/grey"
app:tabIndicatorColor="#color/colorPrimary"
app:tabMode="scrollable"
app:tabSelectedTextColor="#color/colorPrimary"
app:tabTextColor="#color/medium_grey" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
So tabbed layout and a properly setup viewpager. Though even this is going to be kind of obsolete once Google finishes up their Navigation bit in their Architecture Components.

SwiperefreshLayout in Android

i am using SwipeRefreshLayout in my below layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/homePageBackground"
android:orientation="vertical" >
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipeRefreshLayout_listView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<fragment
android:id="#+id/announcementHomefragment"
android:name="in.test.app.AnnouncementFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/homePageBackground" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:background="#color/homePageBackground" >
<TextView
android:id="#+id/newsTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp"
android:gravity="center"
android:text="#string/new_list"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#color/white"
android:textStyle="bold" />
<fragment
android:id="#+id/newshomefragment"
android:name="in.test.app.NewsFragment"
android:layout_width="wrap_content"
android:layout_height="190dp"
android:layout_below="#id/newsTitle"
android:layout_marginTop="-15dp" />
<TextView
android:id="#+id/productTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/newshomefragment"
android:layout_gravity="center"
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp"
android:gravity="center"
android:text="#string/product_in_home"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#color/white"
android:textStyle="bold" />
<fragment
android:id="#+id/proCategoryhomefragment"
android:name="in.test.app.CategoryFragment"
android:layout_width="wrap_content"
android:layout_height="170dp"
android:layout_below="#id/productTitle"
android:layout_marginTop="-15dp" />
<TextView
android:id="#+id/trainingTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/proCategoryhomefragment"
android:layout_gravity="center"
android:layout_marginLeft="15dp"
android:layout_marginTop="2dp"
android:gravity="center"
android:text="#string/trainings_in_home"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#color/white"
android:textStyle="bold" />
<fragment
android:id="#+id/trainingfragment"
android:name="in.test.app.TrainingFragment"
android:layout_width="match_parent"
android:layout_height="180dp"
android:layout_below="#id/trainingTitle"
android:layout_marginBottom="10dp"
android:layout_marginTop="-15dp" />
</RelativeLayout>
</ScrollView>
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
When I pull down my SwipeRefreshLayout it is working, but as you can see in the above code I have a scroll view inside that. So when I am pulling down my scroll view, it goes down and half the images are not showing because it came down. When I am trying to pull up again my scroll view is not going up. Instead, SwipeRefreshLayout is getting call. What should i do?
Please help me out.
I would say it's better to have an extended SwipeRefreshLayout with listener to be able to add various conditions from the classes that display this layout.
Something like the following:
GeneralSwipeRefreshLayout.java
public class GeneralSwipeRefreshLayout extends SwipeRefreshLayout {
private OnChildScrollUpListener mScrollListenerNeeded;
public interface OnChildScrollUpListener {
boolean canChildScrollUp();
}
public GeneralSwipeRefreshLayout(Context context) {
super(context);
}
public GeneralSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Listener that controls if scrolling up is allowed to child views or not
*/
public void setOnChildScrollUpListener(OnChildScrollUpListener listener) {
mScrollListenerNeeded = listener;
}
#Override
public boolean canChildScrollUp() {
if (mScrollListenerNeeded == null) {
Log.e(GeneralSwipeRefreshLayout.class.getSimpleName(), "listener is not defined!");
}
return mScrollListenerNeeded != null && mScrollListenerNeeded.canChildScrollUp();
}
}
And then inside your class that displays SwipeRefreshLayout containing ListView or GridView layout, you can do something like this:
mSwipeLayout.setOnChildScrollUpListener(new OnChildScrollUpListener() {
#Override
public boolean canChildScrollUp() {
return mListView.getFirstVisiblePosition() > 0 ||
mListView.getChildAt(0) == null ||
mListView.getChildAt(0).getTop() < 0;
}
});
Just create a class which extends SwipeRefreshLayout and override the method canChildScrollUp(). Return true when you want scroll down for your control.
For example for scrollview you may try this,
#override.
boolean canChildScrollUp()
{
//your condition to check scrollview reached at top while scrolling
if(scrollview.getScrollY() == 0.0)
return true;
else
return false;
}
As others have already stated, if you don't have your scrollable view (ie listview) as the direct child of the SwipeRefreshLayout, the stock canChildScrollUp will not work.
Has to do with the simple logic SwipeRefreshLayout uses in checking the ability of the child view to scroll.
I was using a ListView inside an ActionbarActivity, and wanted to include an empty view whenever my listview was empty. This caused problems, since the SwipeRefreshLayout class can only have a single child. Note it also checks this child's ability to scrollUp to determine if a pull down causes the child to scrollUp, or if it causes the childs content to refresh.
So if you want to use the same logic as SwipeRefreshLayout, just extend the class, and create a method to allow you to pass in the handle to your scrollable view. Note the stock implementation uses canScrollVertically() which does exactly what we want, but only appears in SDK >= 14.
Also don't forget to include the constructor that contains the param "AttributeSet", when you extend the class, otherwise you will have problems using the class in your layout files.
So, in the onCreate method of your Activity (in my case it was an ActionBarActivity) that includes the list view, just call setMyScrollableView passing in your ListView or whatever view you use that scrolls.
/*Constructor*/
public MySwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
View mMyScrollableView = null; //The content that get's pulled down.
/*Method used to pass in my scrollable view*/
public void setMyScrollableView(View view){
mMyScrollableView = view;
}
/**
* #return Whether it is possible for the child view of this layout to
* scroll up. This was taken for the most part directly from SwipeRefreshLayout
*/
public boolean canChildScrollUp() {
if(mMyScrollableView == null)
return false;
if (android.os.Build.VERSION.SDK_INT < 14) {
if (mMyScrollableView instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mMyScrollableView;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
.getTop() < absListView.getPaddingTop());
} else {
return mMyScrollableView.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mMyScrollableView, -1);
}
}
The solution from #User22791 works perfectly and, based on that, I created a library available on github that you can use (and modify) for make the usage of swipeRefreshLayout easier for developers. It's here: https://github.com/dagova/referencedSwipeRefreshLayout
Basically you just have to reference in your layout the view to be checked in the method canChildScrollUp. I hope it will be useful.
I also found the other answers didn't quite work.
Took me a while of head scratching to figure out that they are using the method getScrollY() which as this answer explains, is a View method describing how far it's been scroll within a container, not a method to describe how much your Scroll container has been scrolled.
If you use the same technique as in the other answers (overriding the canChildScrollUp() method) you can easily check if the Scrollable is at it's highest point:
#Override
public boolean canChildScrollUp() {
return !isListAtTop();
}
private boolean isListAtTop() {
if(mGridView.getChildCount() == 0) return true;
return mGridView.getChildAt(0).getTop() == 0;
}
(As you can see, I'm using a GridView, but you can use a ListView too)
Easier solution is to use onScrollListener and check if user can see firstElement.
someView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
if (isViewAtTop()) {
swipeLayout.setEnabled(true);
} else {
swipeLayout.setEnabled(false);
}
}
}
#Override
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem == 0) {
swipeLayout.setEnabled(true);
} else {
swipeLayout.setEnabled(false);
}
}
});
Where method isViewAtTop() is some other method, that checks this View is scrolled to the top
Ok I have got it working. If the SwipeRefreshLayout is the root of the layout and the ScrollView resides deep into the hierarchy (I had put the ScrollView inside a RelativeLayout) and not the direct child of the SwipeRefreshLayout, it won’t detect a swipe up on the ScrollView properly.
You should create a custom class that extends SwipeRefreshLayout and override canChildScrollUp() method in SwipRefreshLayout
Here is a example :
public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {
private ScrollView scrollview;
public CustomSwipeRefreshLayout(Context context) {
super(context);
}
public CustomSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setView(ScrollView view) {
this.scrollview = view;
}
#Override
public boolean canChildScrollUp() {
return scrollview.getScrollY() != 0;
}
}

Categories

Resources