Android support v4 SwipeRefreshLayout empty view issue - android

SwipeRefresh is not working after setting an empty view for listview which is the only child of a SwipeRefresh layout. How to solve this issue?

Here is the solution:
You can simply use this view hierarchy :
<FrameLayout ...>
<android.support.v4.widget.SwipeRefreshLayout ...>
<ListView
android:id="#android:id/list" ... />
</android.support.v4.widget.SwipeRefreshLayout>
<TextView
android:id="#android:id/empty" ...
android:text="#string/empty_list"/>
</FrameLayout>
Then, in code, you just call:
_listView.setEmptyView(findViewById(android.R.id.empty));
That's it.

I had this issue too, and solved it without any additional code in the activity by using the layout below.
If you are using a ListActivity or ListFragment it handles showing/hiding the empty view for you, and refreshing works with an empty list as well with this layout structure.
No hacks needed, everything is in the SwipeRefreshLayout, as it should be.
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/Refresher"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#null" />
<ScrollView
android:id="#android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Geen formulieren gevonden"
style="#style/text.EmptyView" />
</ScrollView>
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
Hope this helps you. If so, don't forget to accept the answer.
Note: this is a duplicate of my answer to this question, but that question is pretty much a duplicate of this question... :)
UPDATE
If the SwipeRefreshLayout is activated too early, you can implement ListView.OnScroll with:
_refresher.Enabled = e.FirstVisibleItem == 0;
This disables the refresher until you scrolled to the top. This is only needed when using a ListView, the new RecyclerView works as expected.

The problem for me was that when the empty view was visible then the refreshing circle wasn't shown although the refreshing method worked. For me this code worked, I hope it helps.
the xml layout:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true">
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fadingEdge="none"
android:footerDividersEnabled="false"
android:headerDividersEnabled="false"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center">
<TextView
android:id="#+id/empty_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:visibility="gone"/>
</ScrollView>
</FrameLayout>
the custom SwipeRefreshLayout:
package misc;
import android.content.Context;
import android.support.v4.widget.SwipeRefreshLayout;
import android.widget.ListView;
public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {
private ListView mListView;
public CustomSwipeRefreshLayout(Context context) {
super(context);
}
public void setListView(ListView listView) {
mListView = listView;
}
#Override
public boolean canChildScrollUp() {
if (mListView == null) {
return true;
}
return mListView.canScrollVertically(-1);
}
}
and in my Fragment where I use the layout I only set the swipe to refresh layout in this way:
mSwipeRefreshLayout.setListView(mListView);
the ListView's empty view is set in this way:
TextView emptyView = (TextView) view.findViewById(R.id.empty_view);
emptyView.setText("No data");
mListView.setEmptyView(emptyView);
I hope it helps.

here's how i did this (probably not very elegant solution)
private void createEmptyView() {
ViewGroup parent = (ViewGroup) listView.getParent();
emptyView = (TextView) getLayoutInflater(null)
.inflate(R.layout.view_empty, parent, false);
parent.addView(emptyView);
listView.setEmptyView(emptyView);
}
public class FrameSwipeRefreshLayout extends SwipeRefreshLayout {
private ViewGroup listView;
public void setListView(ViewGroup list) {
listView = list;
}
#Override
public boolean canChildScrollUp() {
if (listView != null) {
View child = listView.getChildAt(0);
return child != null && child.getTop() != 0;
}
return super.canChildScrollUp();
}
}
empty layout
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:clickable="true" />
list layout
<FrameSwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
</FrameSwipeRefreshLayout>

You can check out this issue. I posted a work around solution.
Android - SwipeRefreshLayout with empty textview

I fixed this issue using two SwipeToRefreshLayouts.
One for wrapping the ListView
The other as an emptyView
I posted my code on GitHub.

As #Dinesh written above, i have used clickable="true" like below with empty RecyclerView:
mRecyclerView.setVisibility(View.VISIBLE);
mRecyclerView.setClickable(true);
myText.setVisibility(View.VISIBLE);
xml layout:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipeRefreshKartvizitKisilerim"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>
<TextView
android:id="#+id/myText"
android:visibility="gone"
android:text="#string/noListItem"
android:textSize="18sp"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
RecyclerView has height match_parent and SwipeRefresh has wrap_content. When there is item in list, don't forget to make text gone.

I have another idea which works well.
Subclass ListView and override setAdapter() and setEmptyView().
setEmptyView should just store the view to use for the empty view, and
setAdapter should register a DataSetObserver that will hide/show the empty view without altering the list view's visibility. You would probably want to use a FrameLayout that holds your SwipeToRefreshLayout and an empty view (as siblings). You can then position the empty view at the top/middle/bottom etc pretty easily using gravity and layout gravity. You could also use a RelativeLayout but I haven't tried. You will want to somehow unregister the dataSetObserver, I believe you may want to do that in onDetachFromWindow as I did below.
public class SwipeToRefreshCompatibleList extends ListView {
private boolean mIsEmpty = false;
private View mEmptyView;
private Adapter mAdapter;
private DataSetObserver mLocalObserver = new DataSetObserver() {
#Override
public void onChanged() {
boolean isEmpty = mAdapter == null || mAdapter.getCount() == 0;
if (mEmptyView != null) {
if (isEmpty != mIsEmpty) {
mEmptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
}
}
mIsEmpty = isEmpty;
}
};
public SwipeToRefreshCompatibleList(Context context) {
super(context);
}
public SwipeToRefreshCompatibleList(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SwipeToRefreshCompatibleList(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SwipeToRefreshCompatibleList(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public void setAdapter(ListAdapter adapter) {
mAdapter = adapter;
adapter.registerDataSetObserver(mLocalObserver);
super.setAdapter(adapter);
}
#Override
public void setEmptyView(View view) {
mEmptyView = view;
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mLocalObserver);
}
}
}
Example layout file:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.company.widget.SwipeToRefreshCompatibleList
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/BasicList"
android:divider="#null"
/>
</android.support.v4.widget.SwipeRefreshLayout>
<TextView
android:padding="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_gravity="center"
android:textSize="18sp"
android:id="#android:id/empty"
/>
</FrameLayout>
While this takes a bit more code than simply subclassing ListView and altering setVisibility such that it won't set the list to GONE/HIDDEN, I feel like this is less of a hack and still allows the user to set the list to GONE/Hidden if they needed.

I have tried UI hack but it didn't work, the only solution worked is set adapter.
pass empty value make sure the value will not be null.
NotificationListAdapter notificationListAdapter;
notificationListAdapter = new NotificationListAdapter(mContext,notificationResponse);
reCycleViewNotificationList.setAdapter(notificationListAdapter); // weather ListView or RecyclerView

Related

why recyclerview lollipop load everything?

I have a list of 100 items. I'm using a recyclerview with custom adapter.
It seems that all the items are load in the same times which cause OOM because I'am loading big images.
I have no problem with listview because listview does not load everything at once.
I heard that recyclerview load everything at once on lollipop, but how can I fix that?
public class ProductAdapter extends RecyclerView.Adapter<ProductViewHolder> {
List<Produit> list;
private Context ac;
private int nbColumn;
public ProductAdapter(List<Produit> list, Context ac)
{
this.list = list;
this.ac = ac;
this.nbColumn = 2;
}
public ProductAdapter(List<Produit> list, Context ac, int nbColumn)
{
this.list = list;
this.ac = ac;
this.nbColumn = nbColumn;
}
#Override
public ProductViewHolder onCreateViewHolder(ViewGroup viewGroup, int itemType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_product,viewGroup,false);
return new ProductViewHolder(view);
}
public void setNbColumn(int nb)
{
this.nbColumn = nb;
}
#Override
public void onBindViewHolder(final ProductViewHolder myViewHolder, int position) {
if (list != null) {
System.out.println("LOAD ITEM!!!!!!!!!!!!");
final Produit myObject = list.get(position);
myViewHolder.bind(myObject);
}
}
#Override
public int getItemCount() {
return list.size();
}
}
without start scrolling the output is
I/System.out: LOAD ITEM!!!!!!!!!!!!!!!!!
X100 times
UPDATE :
this is the code of my layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="wrap_content"
>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
>
<TextView
android:id="#+id/cat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a title"
android:padding="10dp"
android:gravity="center"
android:textColor="#color/black"
android:textStyle="bold"
android:textSize="17dp"
/>
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="#drawable/list1_unselected"
android:id="#+id/list1"
android:layout_below="#+id/cat"
android:paddingLeft="10dp"
android:clickable="true"
/>
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="#drawable/list2"
android:paddingLeft="10dp"
android:id="#+id/list2"
android:layout_below="#+id/cat"
android:layout_toRightOf="#+id/list1"
android:clickable="true"
/>
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/listingGrid"
android:layout_width="match_parent"
android:nestedScrollingEnabled="false"
android:layout_below="#+id/list2"
android:layout_height="match_parent"/>
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
</RelativeLayout>
I partially figured out my problem. After removing the NestedScrollView, the behavior of my Recyclerview works as expected. The thing is how can I combine both NestedScrollView and RecyclerView?
Thank you
Set android:layout_height of your RecyclerView to match_parent or some constant value.
EDIT:
Obviously it didn't help, since you have it all wrapped in NestedScrollView.
I would suggest removing NestedScrollView and combining all views from your RelativeLayout into another layout that you would use as header in your RecyclerView
Replace if (list != null) with if (myViewHolder != null) and it will work as intended

android soft keyboard overlays the edittext in recyclerview in bottomsheet

I have a few EditText in RecyclerView that is inside of a BottomSheetDialog. The problem I have now is that when BottomSheetDialog is shown on the screen, I tap on for example the 7th EditText in the RecyclerView. Soft keyboard appears and overlays the EditText, so I can't see what I type. but if I dragged the BottomSheetDialog a bit up, EditText then won't be covered by soft keyboard even if I tap on last EditText on the screen. RecyclerView is definitely resized in this case but doesn't resize if I didn't drag BottonSheetDialog a bit up. any idea why? and how I can fix this?
this is how it looks like.
Main.java
class VH extends RecyclerView.ViewHolder {
public VH(View itemView) {
super(itemView);
}
}
private void test() {
BSTest bsTest = new BSTest(this);
bsTest.setContentView(R.layout.bottomsheet_test);
RecyclerView rv = (RecyclerView) bsTest.findViewById(R.id.recyclerView);
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(new RecyclerView.Adapter() {
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new VH(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_edittext, parent, false));
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
}
#Override
public int getItemCount() {
return 20;
}
});
bsTest.show();
}
BSTest.java
public class BSTest extends BottomSheetDialog {
public BSTest(#NonNull Context context) {
super(context);
}
private BSTest(#NonNull Context context, #StyleRes int theme) {
super(context, theme);
}
private BSTest(#NonNull Context context, boolean cancelable, OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
}
}
bottomsheet_test.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="1"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="2"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="3"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
item_edittext.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="1"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="2"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="3"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Using this KeyboardUtil as new solution !
Use this in onCreateDialog in BottomSheetFragment
KeyboardUtil(getActivity(), view);
or
For fragment use
new KeyboardUtil(this, findViewById(R.id.fragment_container));
by using this Util class
https://github.com/mikepenz/MaterialDrawer/blob/aa9136fb4f5b3a80460fe5f47213985026d20c88/library/src/main/java/com/mikepenz/materialdrawer/util/KeyboardUtil.java
Credit:Mikepenz
It looks that editing your Manifest and adding android:windowSoftInputMode="adjustResize" to <activity> tag of this activity should solve your problem.
Docs: https://developer.android.com/guide/topics/manifest/activity-element.html#wsoft

Large gap forms between RecyclerView items when scrolling down [duplicate]

This question already has answers here:
RecyclerView items with big empty space after 23.2.0
(5 answers)
Closed 3 months ago.
I'm making a ToDo list app, and while testing it, for some reason, a huge gap forms between the items whenever I try to scroll down. It always happens whenever I Drag and Drop the items, but I don't see any errors with my ItemTouchHelper adapter and callback class. It would be awesome if you can help me out.
Before:
After:
RecyclerAdapter.java
public class RecyclerAdapter extends
RecyclerView.Adapter<RecyclerAdapter.RecyclerVH> implements ItemTouchHelperAdapter{
private LayoutInflater layoutInflater;
ArrayList<Info> data;
Context context;
public RecyclerAdapter(Context context) {
layoutInflater = LayoutInflater.from(context);
this.context = context;
}
public void setData(ArrayList<Info> data) {
this.data = data;
notifyDataSetChanged();
}
#Override
public RecyclerVH onCreateViewHolder(ViewGroup viewGroup, int position) {
View view = layoutInflater.inflate(R.layout.custom_row, viewGroup, false);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("R.A onClick Owen", "onClick method triggered");
}
});
RecyclerVH recyclerVH = new RecyclerVH(view);
return recyclerVH;
}
#Override
public void onBindViewHolder(RecyclerVH recyclerVH, int position) {
Log.d("RecyclerView", "onBindVH called: " + position);
final Info currentObject = data.get(position);
// Current Info object retrieved for current RecyclerView item - USED FOR DELETE
recyclerVH.listTitle.setText(currentObject.title);
recyclerVH.listContent.setText(currentObject.content);
/*recyclerVH.listTitle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Open new Activity containing note content
Toast.makeText(this, "Opening: " + currentObject.title, Toast.LENGTH_LONG).show();
}
});*/
}
public void deleteItem(int position) {
DBInfo dbInfo = new DBInfo(context);
dbInfo.deleteNote(data.get(position));
// Deletes RV item/position's Info object
data.remove(position);
// Removes Info object at specified position
notifyItemRemoved(position);
// Notifies the RV that item has been removed
}
#Override
public int getItemCount() {
return data.size();
}
// This is where the Swipe and Drag-And-Drog methods come into place
#Override
public boolean onItemMove(int fromPosition, int toPosition) {
// Swapping positions
// ATTEMPT TO UNDERSTAND WHAT IS GOING ON HERE
Collections.swap(data, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
return true;
}
#Override
public void onItemDismiss(int position) {
// Deleting item from RV and DB
deleteItem(position);
}
class RecyclerVH extends RecyclerView.ViewHolder implements View.OnClickListener{
// OnClickListener is implemented here
// Can also be added at onBindViewHolder above
TextView listTitle, listContent;
public RecyclerVH(View itemView) {
super(itemView);
listTitle = (TextView) itemView.findViewById(R.id.title);
listContent = (TextView) itemView.findViewById(R.id.content);
listTitle.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Toast.makeText(context, "Opening: Note" + getLayoutPosition(), Toast.LENGTH_SHORT).show();
// PS NEVER ADD listTitle VARIABLE AS PUBLIC VARIABLE ABOVE WHICH IS GIVEN VALUE AT ONBINDVH
// THIS IS BECAUSE THE VALUE WILL CHANGE IF ITEM IS ADDED OR DELETED
}
}
}
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fab="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"
android:orientation="vertical"
android:weightSum="1">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/rounded_corners" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerList"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.melnykov.fab.FloatingActionButton
android:id="#+id/fab_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginBottom="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:gravity="bottom|end"
android:onClick="addNote"
android:src="#drawable/fab_ic_add"
fab:fab_colorNormal="#color/colorPrimary"
fab:fab_colorPressed="#color/colorPrimaryDark"
fab:fab_colorRipple="#color/colorPrimaryDark" />
</RelativeLayout>
</FrameLayout>
</LinearLayout>
custom_row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/main"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="8dp"
android:text="#string/test"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/main"
android:paddingLeft="8dp">
<TextView
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/test"
android:textSize="15sp" />
</LinearLayout>
</RelativeLayout>
Thank you so much to whoever can help me out. I am pulling my hair out as I type.
EDIT: I have confirmed that it is not my ItemTouchHelper class that's the problem. (Tried running without it being called, problem still occurs.)
Also, it seems that when a dialog is shown and the keyboard brought up, the RecyclerView in the background resolves the problem by itself. After dialog is removed, the problem repeats (i.e. Scrolling puts massive space between items)
change in Recycler view match_parent to wrap_content:
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Also change in item layout xml
Make parent layout height match_parent to wrap_content
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
This happened to me many times!
All you need to do is... make layout_height of row file wrap_content and your problem solved..
android:layout_height="wrap_content"
Happy coding!
Its because you are using match_parent in height of root view of the row item in your vertically oriented listview/recyclerview. When you use that the item expands completely wrt to its parent.
Use wrap_content for height when the recyclerview is vertically oriented and for width when it is horizantally oriented.
Avoid taking the view in item layout with a container like this:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data/>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:listPreferredItemHeight"
android:textAppearance="?android:attr/textAppearanceMedium" />
</android.support.constraint.ConstraintLayout>
</layout>
as in this case match_parent will do its work and the problem will still remain! Rather take it like this:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data/>
<TextView
android:id="#+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:listPreferredItemHeight"
android:textAppearance="?android:attr/textAppearanceMedium" />
</layout>
[Note: Above code has data binding attached to it, don't use <layout> & <data> tags if not using data binding]
Other than this, if you must use any view group containers, than take a look the height and width attributes in the container, and try change those from match_parent to wrap_content. That should resolve the issue. For more transparency, one can try giving background colours and see through it to identify actual problem.
Just for the record: Since in my case we had to implement some "superfancy UI" with overlapping RecyclerView and blur effect toolbar, I had to get rid of clipToPadding="false" within RecyclerView-xml.
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="#dimen/height_toolbar"
android:paddingBottom="#dimen/height_bottom_bar_container"
//android:clipToPadding="false" <-- remove this flag!
android:scrollbars="vertical"
/>
paddingTopand paddingBottom worked, but we replaced it with some GapViews(empty views, with height of paddingTop and paddingBottom)
if someone is using | recyclerViewObject.setHasFixedSize(true);
try removing it, It was causing the issue in my case.
You can try setting your Viewgroup holding the recyclerview to wrap content
i have this problem and i just use
android:layout_height="wrap_content"
for parent of every item.

Non Scroll RecyclerView scrolling issue in Android SDK 23

I am implementing a RecyclerView inside a ScrollView. In order to have only one scrolling behaviour on the entire page I implement a NonScrollRecyclerView version. The implementation is as follows:
public class NonScrollRecyclerView extends RecyclerView {
public NonScrollRecyclerView(Context context) { super(context); }
public NonScrollRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NonScrollRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev){
if(ev.getAction() == MotionEvent.ACTION_MOVE)
return true;
return super.dispatchTouchEvent(ev);
}
}
Once i update my build and target settings to SDK 23, I have trouble scrolling the page which contains the NonScrollRecyclerView. The specific problem is that the page scrolls OK until i reach the recycler view portion and once i scroll onto this view I am unable to scroll anymore, either up or down.
I DONOT face this problem with SDK 22 and below
My xml is as follows:
XML #layout/rv contains the recycler view
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/background_gray">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/background_gray"
android:orientation="vertical">
<include
layout="#layout/row_mall_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/cv_mall_header"
android:layout_marginTop="8dp"/>
<include
layout="#layout/row_mall_shops"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/cv_mall_shops"
android:layout_marginTop="8dp"/>
<include
layout="#layout/row_mall_coupons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/cv_mall_coupons"
android:layout_marginTop="8dp"/>
<include
layout="#layout/rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/cv_mall_feeds"
android:layout_marginTop="8dp"/>
</LinearLayout>
</ScrollView>
XML - #layout/rv
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/background_gray"
android:id="#+id/ll_mall_feeds">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:paddingTop="6dp"
android:paddingBottom="6dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tv_feedcount"
android:textColor="#color/semi_theme_blue"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginLeft="12dp"
android:layout_centerVertical="true" />
</RelativeLayout>
<com.project.ui.NonScrollRecyclerView
android:id="#+id/nrv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#android:color/transparent"
android:listSelector="#android:color/transparent" />
</LinearLayout>
RecyclerView and ListView are not recommended inside a ScrollView because elements hights are not calculated when rendering the ScrollView. This means, your adapter might not be populated when the ScrollView is shown and later when the RecyclerView is notified about data changes (for instance when you initialize your adapter), there's not way to recalculate the elements heights.
It's really a pain in the neck because you have to try to calculate the elements heights and it's never accurate, so you will have discrepancies when showing a ListView or a RecyclerView inside a ScrollView. Some examples of how to calculate the elements heights can be checked here and here.
My advice is to turn your RecyclerView into a LinearLayout and add elements programmatically, so you emulates the ListView or RecyclerView behaviour:
LinearLayout layout = (LinearLayout) rootView.findViewById(R.id.files);
layout.removeAllViews();
for (int i = 0; i < fileAdapter.getCount(); i++) {
final View item = fileAdapter.getView(i, null, null);
item.setClickable(true);
item.setId(i);
item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fileContentRowPosition = v.getId();
# Your click action here
}
});
layout.addView(item);
}
Here its the XML with the files definition:
<ScrollView
android:id="#+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
<LinearLayout
android:id="#+id/files"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
The whole java code can be checked here and the whole Layout here.
On the other hand, if you still want to continue with your implementation, and regarding your issue, you can check this article about Handling Scrollable Controls in Scrollview
Best regards,
For only one scrolling in the entire page you can use NestedScrollView instead of ScrollView and set recyclerView.setNestedScrollingEnabled(false);

ListView item doesn't inflate to wrap content as intended, height == 0 but shouldn't

I am trying to incorporate 2 lists "sections" into a LinearLayout via height weights. The issue that I am having is that, though the container for the "section" is displayed with the proper height (where the background is sub_gray), and the adapter count is correct, the height of my actual list rows is 0 which makes my list appear invisible.
LIST ITEM
Below is the code that my list rows/items directly work with. To avoid the wall of code getting too long, I will include my code for one of the lists seeing as they are both behaving the same.
layout_quick_guide_checklist_item
<?xml version="1.0" encoding="utf-8"?>
<com.my_project.project.QuickGuideChecklistListItemView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="50dp"
android:background="#color/sub_gray"
android:orientation="horizontal" >
<CheckBox
android:id="#+id/checkbox_checklist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_gravity="center"
android:checked="true"
android:clickable="false" />
<TextView
android:id="#+id/text_field"
style="#style/default_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="#string/height_wrap_string"/>
</com.my_project.project.QuickGuideChecklistListItemView>
QuickGuideChecklistListItemView
public class QuickGuideChecklistListItemView extends LinearLayout{
private TextView listText;
public QuickGuideChecklistListItemView(Context context) {
super(context);
}
public QuickGuideChecklistListItemView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public QuickGuideChecklistListItemView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public static QuickGuideChecklistListItemView inflate(ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) parent.getContext().
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
QuickGuideChecklistListItemView itemView = (QuickGuideChecklistListItemView)
inflater.inflate(R.layout.layout_quick_guide_checklist_item, parent, false);
return itemView;
}
#Override
public void onFinishInflate(){
super.onFinishInflate();
listText = (TextView) findViewById(R.id.text_field);
}
public void setText(String text){
listText.setText(text);
}
}
COMPLETE LIST
Now I take the above code and use it within my activity to create a list (recall; there are actually two lists in my activity, constructed similarly, and both resulting in row heights of 0).
QuickGuideDetailActivity
public class QuickGuideDetailActivity extends NavigationDrawerActivity implements OnClickListener{
private final String TAG = getClass().getSimpleName();
private QuickGuideModel selectedQuickGuide;
private QuickGuideChecklistAdapter checklistAdapter;
private QuickGuideTipAdapter tipsAdapter;
private TextView quickGuidesTitle, quickGuidesDescription, inflaterButton;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
selectedQuickGuide = QuickGuidesActivity.selectedQuickGuide;
buildLists();
}
#Override
protected int getLayoutId(){
return R.layout.sub_activity_quick_guide;
}
public void checkAdapterSize(View v){
String checklistString = (checklistAdapter == null)? "null": "" + checklistAdapter.getCount();
String tipString = (tipsAdapter == null)? "null": "" + tipsAdapter.getCount();
Toast.makeText(this, checklistString + " : " + tipString,
Toast.LENGTH_SHORT).show();
}
private void buildLists(){
TextView checklistTitle = (TextView) findViewById(R.id.checklist_title_bar);
checklistTitle.setText(getResources().getString(R.string.title_checklist));
ArrayList<String> checklistStrings = new ArrayList<String>();
for(Object guide : selectedQuickGuide.checklist){
checklistStrings.add((String) guide.toString());
}
checklistAdapter = new QuickGuideChecklistAdapter(checklistStrings);
ListView checklist = (ListView) findViewById(R.id.checklist_list);
checklist.setAdapter(checklistAdapter);
Log.d(TAG, "List Size - Checklist = " + selectedQuickGuide.checklist.length);
TextView tipsTitle = (TextView) findViewById(R.id.tips_title_bar);
tipsTitle.setText(getResources().getString(R.string.title_tips));
tipsAdapter = new QuickGuideTipAdapter(selectedQuickGuide.topTips);
ListView tips = (ListView) findViewById(R.id.tips_list);
tips.setAdapter(tipsAdapter);
Log.d(TAG, "List Size - Tips = " + selectedQuickGuide.topTips.length);
}
}
sub_activity_quick_guide
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
style="#style/default_container"
android:layout_margin="15dp"
android:orientation="vertical" >
<include
android:id="#+id/expandable_section"
layout="#layout/section_expandable_details" />
<LinearLayout
style="#style/default_container"
android:orientation="vertical"
android:weightSum="2">
<LinearLayout
android:id="#+id/checklist_section"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginBottom="8dp"
android:background="#color/sub_gray"
android:onClick="checkAdapterSize" >
<View
android:layout_width="1dp"
android:layout_height="15dp" />
<TextView
android:id="#+id/checklist_title_bar"
style="#style/default_label_text_view"
android:background="#color/random_blue"
android:text="#string/loading" />
<ListView
android:id="#+id/checklist_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#color/off_white"
android:dividerHeight="1dp" />
</LinearLayout>
<LinearLayout
android:id="#+id/tips_list_section"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="7dp"
android:background="#color/sub_gray"
android:onClick="checkAdapterSize" >
<View
android:layout_width="1dp"
android:layout_height="15dp" />
<TextView
android:id="#+id/tips_title_bar"
style="#style/default_label_text_view"
android:background="#color/random_blue"
android:text="#string/loading" />
<ListView
android:id="#+id/tips_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#color/off_white"
android:dividerHeight="1dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<RelativeLayout
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ListView
android:id="#+id/nav_drawer_list_view"
android:layout_width="#dimen/drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#android:color/white" />
</android.support.v4.widget.DrawerLayout>
QuickGuideChecklistAdapter
public class QuickGuideChecklistAdapter extends BaseAdapter{
private ArrayList<String> guides;
private List<Boolean> checklistItemTicked = new ArrayList<Boolean>();
public QuickGuideChecklistAdapter(ArrayList<String> guides){
this.guides = guides;
for(int i = 0; i < guides.size(); i++)
checklistItemTicked.add(false);
}
#Override
public int getCount() {
return guides.size();
}
#Override
public String getItem(int position) {
if(position < 0 || position >= guides.size())
throw new IndexOutOfBoundsException("Position is out of range of list with size " + guides.size());
return guides.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
QuickGuideChecklistListItemView itemView = (QuickGuideChecklistListItemView)convertView;
if (null == itemView)
itemView = QuickGuideChecklistListItemView.inflate(parent);
itemView.setText(guides.get(position));
Log.d(getClass().getSimpleName(), "Setting text to " + guides.get(position) + " in position " + position);
return itemView;
}
}
Sorry for the wall of code, but I have tried adjusting everything and just can't seem to get anything to work. So just as a final bit:
The adapters are set, getCount() returns the valid count of items in the list and clicking on the layouts in order to call checkAdapterSize() also results in a Toast with the proper values.
The first view is "inflated" but results in a view height of 0.
The container layouts for my list "sections" (checklist_section in sub_activity_quick_guide for example) have a height which can be seen by the sub_gray background.
I have tried changing height attributes from match_parent to wrap_content and vice versa with no luck. I have tried explicitly declaring a minHeight for my list items and all that good stuff. I just can't seem to ever get a list row item to actually inflate.
Funny how I always manage to figure out an answer to my own questions right after I post a question on SO after days of trying :P
Anyway, the issue was in placing my ListViews inside of LinearLayouts with heights determined by weight. I removed the LinearLayout wrappers for my "sections" and applied the weights to my ListViews directly, voila... good to go.
New complete working Activity layout
sub_activity_quick_guide
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
style="#style/default_container"
android:layout_margin="15dp"
android:orientation="vertical"
android:weightSum="2" >
<include
android:id="#+id/expandable_section"
layout="#layout/section_expandable_details" />
<View
android:layout_width="1dp"
android:layout_height="15dp" />
<TextView
android:id="#+id/checklist_title_bar"
style="#style/default_label_text_view"
android:background="#color/random_blue"
android:text="#string/loading" />
<ListView
android:id="#+id/checklist_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:divider="#color/off_white"
android:dividerHeight="1dp" />
<View
android:layout_width="1dp"
android:layout_height="15dp" />
<TextView
android:id="#+id/tips_title_bar"
style="#style/default_label_text_view"
android:background="#color/random_blue"
android:text="#string/loading" />
<ListView
android:id="#+id/tips_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:divider="#color/off_white"
android:dividerHeight="1dp" />
</LinearLayout>
<RelativeLayout
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ListView
android:id="#+id/nav_drawer_list_view"
android:layout_width="#dimen/drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#android:color/white" />
</android.support.v4.widget.DrawerLayout>
Hope it helps someone in the future, didn't realize weights would mess with it like that but apparently ListViews don't like weighted containers.

Categories

Resources