I am developing an android application for listing the products of an on-line store. For this I have used a GridView for that. But inside a category could be a big number of products, so I have to implement a feature like, "Loading on demand", with a button at the end of a gridView telling (Load More). Of course this button will be visible only if the GridView has reached the end of the items. But I dont how to do it. Below is my solution so far, but the button is not visible when gridview goes at the end.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<com.twotoasters.jazzylistview.JazzyGridView
android:id="#+id/ebuy_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:drawSelectorOnTop="true"
android:horizontalSpacing="15dp"
android:numColumns="2"
android:verticalSpacing="15dp" />
<TextView
android:id="#+id/more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:background="#drawable/ebuy_now_background"
android:clickable="true"
android:onClick="loadMore"
android:padding="15dp"
android:text="Load More"
android:textColor="#color/ebuy_color_second_strip"
android:textSize="25sp" />
</LinearLayout>
</ScrollView>
Suggestions or other solution will be more than welcome :)
as you implement and add this listener to your Gridview this will detect for the end of Gridview as scroll position was at end of list just get more data. And during the loading data you need one flag to load data at once when the scroll position goes to end. So if data is loading and during that time you scroll up then scroll down then it will not get more data for dupplication.
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
this.currentFirstVisibleItem = firstVisibleItem;
this.currentVisibleItemCount = visibleItemCount;
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.currentScrollState = scrollState;
this.isScrollCompleted();
}
private void isScrollCompleted() {
if (this.currentVisibleItemCount > 0 && this.currentScrollState == SCROLL_STATE_IDLE) {
/*** In this way I detect if there's been a scroll which has completed ***/
/*** do the work for load more date! ***/
if(!isLoading){
isLoading = true;
loadMoreDAta();
}
}
}
Make "Load More" button part of the GridView - in your getView method inflate a layout with button when "position" corresponds to last item.
Related
I have a recyclerview inside SwipeRefreshLayout. and at the end of recyclerview, I'd like to add a button, but I'm unable to do so. Here the code :
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/refresh"
android:layout_width="wrap_content"
android:layout_below="#+id/toolbar"
android:paddingBottom="50dp"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"/>
<Button
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="#+id/load_more"
android:layout_alignParentBottom="true"
android:textColor="#color/md_black_1000"
android:text="TEST"/>
</android.support.v4.widget.SwipeRefreshLayout>
The button does not appear at the end of the recyclerview.
SwipeRefreshLayout could have only one child.
Add RecyclerView and Button to vertical LinearLayout and put it into SwipeRefreshLayout.
<android.support.v4.widget.SwipeRefreshLayout>
<LinearLayout>
<android.support.v7.widget.RecyclerView>
<Button>
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
To add Button as last item in RecyclerView you need edit adapter:
1) increase number of rows
#Override
public int getItemCount() {
return pictureArrayList.size() + 1;
}
2) show Button when list ends
#Override
public void onBindViewHolder(final ExampleHolder holder, final int position) {
final Picture picture = pictureArrayList.get(position);
if (position <= pictureArrayList.size()) {
holder.title.setVisibility(View.VISIBLE);
holder.button.setVisibility(View.GONE);
holder.title.setText(picture.getName());
holder.imageView.setImageResource(picture.getImage());
} else {
holder.title.setVisibility(View.GONE);
holder.button.setVisibility(View.VISIBLE);
}
}
You need to create a RecyclerView with different view items depending on what you want to do may be an option to use a view button at the bottom of the list, I have a blog post where I wrote a blog about the RecyclerView(Spanish).
http://erikcaffrey.github.io/2015/10/05/recyclerview/
You can see the code!
https://github.com/erikcaffrey/RecyclerView-Examples
I have 3 views: an ImageView, a RelativeLayout and a ListView. When the user scrolls down, I have to hide the ImageView first. When the top of the RelativeLayout touches the top of the screen it has to fix itself there and the scroll must be done by the ListView. Then when I scroll up, the top of the ListView must be visible before the ImageView starts to scroll down again.
Is there any component that does what I want without having to write my own component?
(Just to make things easier, it should work like the headers in Instagram, when the header reaches the top it stays there, but there should be only one header)
I have a very simple solution to this, hope it helps. I use one header added to the listView and the same header added to the page on top. There is a listener to toggle visibility of the fixed header. Everything is done in the onCreate method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.list);
LayoutInflater inflater = getLayoutInflater();
// your image header
View headerImage = inflater.inflate(R.layout.header_image, listView, false);
listView.addHeaderView(headerImage);
// the header that scrolls with the listview
final View fixedHeader = inflater.inflate(R.layout.header_fixed, listView, false);
listView.addHeaderView(fixedHeader);
// the header that is fixed on top of the screen
final View secondFixedHeader = findViewById(R.id.fixed_header);
secondFixedHeader.setVisibility(View.GONE);
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem > 0) {
secondFixedHeader.setVisibility(View.VISIBLE);
} else {
secondFixedHeader.setVisibility(View.GONE);
}
}
});
listView.setAdapter(new ListAdapter(this));
}
there is the activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include
android:id="#+id/fixed_header"
layout="#layout/header_fixed" />
</RelativeLayout>
there is the header_fixed.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="#android:color/darker_gray">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a fixed header"
android:textAppearance="#android:style/TextAppearance.DeviceDefault.Large"/>
</RelativeLayout>
and there is the header_image.xml:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="#drawable/android"/>
I'm implementing a "parallax" effect which consists of a list view and an image view above.
When the user scrolls downwards, the offset is subtracted from the height of the image view. This works fine. I'm doing this in the scroll listener of the list view. To really update the listview and imageview in realtime I need to call requestLayout().
The problem is, that this method is asynchronous and if I scroll slowly it flickers extremely. If I just swipe quickly it looks very nice. Is there an other solution to solve this?
ScrollListener
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
View c = view.getChildAt(0);
int top = (c == null) ? 0 : (-c.getTop() + view.getFirstVisiblePosition() * c.getHeight());
//Log.d(TAG, "Offset = " + top);
mImageView.getLayoutParams().height = 400 - top;
mImageView.requestLayout();
}
Layout
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context="xxx.fragments.NewsFragment">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#android:color/holo_blue_light"
android:id="#+id/advertisement"
/>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/news_feed">
</ListView>
</TableLayout>
If you just want the ImageView to scroll with the list, then you can simply add it as a header view:
listView.addHeaderView(mImageView);
Make sure you call this method before calling setAdapter. Also make sure your ImageView doesn't already have a parent, so inflate it separately or just create one in code using new ImageView.
I have an activity which contains a ScrollView, and also I have a GridView inside the ScrollView, the layout:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/root">
<RelativeLayout
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
.........
<com.test.android.view.ScrollableGridView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:numColumns="auto_fit"
android:columnWidth="100dp"
android:verticalSpacing="2dp"
android:focusable="false"
android:clickable="false">
</com.test.android.view.ScrollableGridView>
</RelativeLayout>
</ScrollView>
public class ScrollableGridView extends GridView {
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
}
Why I use the custome gridview is to make sure the gridview can expand to its max height(check this).
Now once the activity loaded, I will load data from the server, then call the:
gridAdapter.notifyDatasetChanged();
Then the activity will scroll to the grid view which means user can not see the content above the gridview.
I have tried that:
mScrollView.scrollTo(0,mScrollView.getBottom());
But it does not work.
Any idea to fix it?
Issue:
GridView is being scrolled automatically.
Reason:
When the screen is loaded, it finds the first focusable View from its ViewHierarchy and sets the focus to that View.
Solution:
You can set focus to some other View so that Android does not focus the GridView at first,so it won't scroll automatically.
Example:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/root">
<RelativeLayout
android:padding="10dp"
android:id="#+id/rlContainer" //you may set id and requestfocus from java code as well
android:focusable="true" //add this
android:focusableInTouchMode="true" //add this
android:layout_width="match_parent"
android:layout_height="match_parent">
......... //you can also add focusable,focusableInTouchMode to any other view before the GridView
<com.test.android.view.ScrollableGridView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:numColumns="auto_fit"
android:columnWidth="100dp"
android:verticalSpacing="2dp"
android:focusable="false"
android:clickable="false">
</com.test.android.view.ScrollableGridView>
</RelativeLayout>
From java code,
inside onCreate() method of your Activity
RelativeLayout rlContainer = (RelativeLayout) findViewById(R.id.rlContainer);
rlContainer.requestFocus();
A better option will be to scroll to the GridView's top. Also, you should post the scrollTo(int, int) call:
mScrollView.post(new Runnable() {
#Override
public void run() {
mScrollView.scrollTo(0, mGridView.getTop());
}
});
Edit:
So, from what I can gather:
on first load, GridView is at the top & visible
then, you load some data from the server
some layout container above the GridView is updated with data - this increases the layout's size and the GridView is pushed down
Lets say that the layout container above the GridView is mLayoutContainer. After adding data to this container, add a OnPreDrawListener to it:
mLayoutContainer.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
// remove the OnPreDrawListener
mLayoutContainer.getViewTreeObserver().removeOnPreDrawListener(this);
// update scrollY for the ScrollView
// since mLayoutContainer is about to be drawn, its height
// is available.
mScrollView.setScrollY(mScrollView.getScrollY()
+ mLayoutContainer.getHeight());
// we're allowing the current draw pass
return true;
}
});
This is basically a state-restore operation. We are asserting that prior state was perfect - state changed resulting in the GridView being pushed down - counter state change by scrolling ScrollView by an equal amount.
got myself into a pickle trying to squeeze two ListViews in the same activity. It works, using two separate ListFragments contained in a standard (vertical) LinearLayout.
The problem is, the two lists together are longer than the screen and the second list is therefore partially hidden. Visually, the user expects to drag the whole screen up and unveil the second list. But the two lists have their own internal scrolling and they do not allow for the whole screen to scroll as one piece.
Luckily the lists actually contain very few items (5 each on average). So, theoretically, I could populate a couple of LinearLayouts containers instead. The problem is, the data being displayed by the lists comes from a Cursor and is dynamic. While I am aware of the newView() and bindView() methods of the CursorAdapter, I don't quite understand how I can connect the adapter to the LinearLayout containers instead of ListViews. I.e. how does the CursorAdapter know that it must create 5 row items out of the 5 items it finds in its cursor? Where do I create the loop that iterates over the cursor item and creates the items in the LinearLayout container? And how do I refresh the content of the LinearLayout when the data in the Cursor changes? All the examples I'm finding neatly wrap these issues into the ListView provided by the ListActivity, but I can't use ListViews!
I'm confused!
Manu
EDIT : Here is the xml layout of the (Fragment)Activity when following breceivemail suggestion. Commented out is the original LinearLayout container, prior to breceivemail's suggestion. It should also be noted the the whole activity is in turn contained by a TabHost, but I don't know if that make any difference for the problem at hand.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<!--
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
-->
<TextView android:id="#+id/title"
android:textSize="36sp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/SelectPlayer"/>
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/Playing"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#000000"
android:background="#999999"/>
<fragment android:name="com.myDomain.myApp.PlayerListFragment"
android:id="#+id/playing"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/Reserve"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#000000"
android:background="#999999"/>
<fragment android:name="com.myDomain.myApp.PlayerListFragment"
android:id="#+id/reserve"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
Put your listViews in a vertical scroll. You can have scrollable listView inside of a vertical scroll by the following trick. use the following code and enjoy!
private int listViewTouchAction;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
setListViewScrollable(myListView1);
setListViewScrollable(myListView2);
}
private void setListViewScrollable(final ListView list) {
list.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
listViewTouchAction = event.getAction();
if (listViewTouchAction == MotionEvent.ACTION_MOVE)
{
list.scrollBy(0, 1);
}
return false;
}
});
list.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view,
int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE)
{
list.scrollBy(0, -1);
}
}
});
}
listViewTouchAction is a global integer value.