View animated using CoordinatorLayout and AppBarLayout getting crazy (sometimes) - android

This thing is getting me nuts.
I have been able to get this behavior (exactly what I want): http://i.imgur.com/PGhL22k.gif
And this is the behavior is has when I scroll down very fast: http://i.imgur.com/kk7icAc.gif and http://i.imgur.com/YNPNiA6.gif
I am sorry but the GIFs are larger than 2Mb and I cannot upload them here...
I want the pagination bar at the bottom to hide the same amount the toolbar does. When scrolling slowly it goes very well, but when fast scrolling it has a really strange behavior as you can see at the GIFs provided above.
This is the layout XML:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:kiosk="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include
layout="#layout/android_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
kiosk:layout_scrollFlags="scroll|enterAlways"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/vpPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
kiosk:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<include
layout="#layout/paginator_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
kiosk:layout_behavior="carl.fri.fer.views.KioskPaginator.KioskPaginatorScrollBehaviour"/>
</android.support.design.widget.CoordinatorLayout>
The "android_toolbar" include is as follows:
<?xml version="1.0" encoding="utf-8"?>
<carl.fri.fer.views.KioskToolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/primaryColor"
android:minHeight="?attr/actionBarSize"/>
And the "paginator_layout" is the following:
<?xml version="1.0" encoding="utf-8"?>
<carl.fri.fer.views.KioskPaginator.KioskPaginator
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/kpPaginator"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:animateLayoutChanges="true"
android:background="#color/primaryColor"
android:clickable="true"
android:orientation="horizontal">
<TextView
android:id="#+id/tvCurrentPage"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center"
android:padding="10dp"
android:textColor="#color/color_pure_black"
android:textSize="17sp"/>
<LinearLayout
android:id="#+id/llMoreOptions"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:background="#android:color/transparent"
android:gravity="center_vertical"
android:orientation="horizontal"/>
<TextView
android:id="#+id/tvTotalPages"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center"
android:padding="10dp"
android:textColor="#color/color_pure_black"
android:textSize="17sp"/>
</carl.fri.fer.views.KioskPaginator.KioskPaginator>
And the ScrollBehavior is as follows:
public class KioskPaginatorScrollBehaviour extends AppBarLayout.ScrollingViewBehavior {
public KioskPaginatorScrollBehaviour(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof AppBarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
float depY = - dependency.getHeight();
depY -= dependency.getY();
Utils.log("dependency", "" + depY);
child.setTranslationY(depY);
return true;
}
}
Inside of every page of the ViewPager, there is a Fragment. And the content of this Fragment is a RecyclerView with dynamic content. All the content from the RecyclerView is loaded from database and images are loaded at runtime.
Can someone please tell me why is this strange behavior happening? How can I fix it?
Thank you in advance...
EDIT 1:
I just spotted the cause of the weird behavior! The ViewPager loads the current page and the adjoining ones. The content of the RecyclerView loads from the Internet and as soon as it is loaded, it goes into the RecyclerView. The ViewPager first loads the current page and then the adjoining ones. If I have scrolled down the current page RecyclerView (Toolbar is hidden) when the 2nd RecyclerView just loads and shows the content, it resets the current page AppBarLayout behavior, so it resets my custom view behavior... How can I fix that? I want to avoid not loading adjoining views..
EDIT 2:
Ok, it happens when loading adjoining pages of ViewPager and also when loading from the Internet images inside the RecyclerView... this is crazy.

So finally resolved your problem - make your custom behavior extending CoordinatorLayout.Behavior instead of ScrollingViewBehavior and it will work as expected. Just set the value to your view's translationY, that is opposite to Y of AppBarLayout:
public class KioskPaginatorScrollBehaviour extends CoordinatorLayout.Behavior<View> {
public KioskPaginatorScrollBehaviour(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof AppBarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
float depY = -dependency.getY();
child.setTranslationY(depY);
return true;
}
}

Related

Custom expandable card with child views

I am new to Android development and feel like this is a really trivial problem, but I cannot word it well enough to find a solution online, so I might as well ask the question here.
My goal is to create a reusable component that is essentially an expandable card like the one described here: https://material.io/design/components/cards.html#behavior.
To do it, I created a custom view that extends a CardView:
public class ExpandableCardView extends CardView {
public ExpandableCardView(Context context) {
super(context);
}
public ExpandableCardView(Context context, AttributeSet attrs) {
super(context, attrs);
// get custom attributes
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ExpandableCardView, 0, 0);
String heading = array.getString(R.styleable.ExpandableCardView_heading);
array.recycle();
// inflate the layout
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.expandable_card_view, this, true);
// set values
TextView headingTextView = findViewById(R.id.card_heading);
headingTextView.setText(heading.toUpperCase());
// set collapse/expand click listener
ImageView collapseExpandButton = findViewById(R.id.collapse_expand_card_button);
collapseExpandButton.setOnClickListener((View v) -> toggleCardBodyVisibility());
}
private void toggleCardBodyVisibility() {
LinearLayout description = findViewById(R.id.card_body);
ImageView imageButton = findViewById(R.id.collapse_expand_card_button);
if (description.getVisibility() == View.GONE) {
description.setVisibility(View.VISIBLE);
imageButton.setImageResource(R.drawable.ic_arrow_up);
} else {
description.setVisibility(View.GONE);
imageButton.setImageResource(R.drawable.ic_arrow_down);
}
}
}
And the layout:
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/expandable_card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="16dp"
android:animateLayoutChanges="true"
app:cardCornerRadius="4dp">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/card_header"
android:padding="12dp"
android:layout_width="match_parent"
android:layout_height="48dp"
android:orientation="horizontal" >
<TextView
android:id="#+id/card_heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="#color/colorPrimary"
android:layout_alignParentLeft="true"
android:text="HEADING"/>
<ImageView
android:id="#+id/collapse_expand_card_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
app:srcCompat="#drawable/ic_arrow_down"/>
</RelativeLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/card_body"
android:padding="12dp"
android:layout_marginTop="28dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:visibility="gone" >
</LinearLayout>
</androidx.cardview.widget.CardView>
Ultimately I want to be able to use it like so in my activities, usually multiple instances per activity:
<xx.xyz.yy.customviews.ExpandableCardView
android:id="#+id/card_xyz"
android:layout_width="match_parent"
android:layout_height="match_parent"
custom_xxx:heading="SOME HEADING" >
<SomeView></SomeView>
</xx.xyz.yy.customviews.ExpandableCardView>
Where SomeView is any text, image, layout or another custom view altogether, typically with data bound from the activity.
How do I get it to render SomeView inside the card body? I want to take whatever child structure is defined within the custom view and show it in the card body when it is expanded. Hope I made it easy to understand.
I think that a better approach would be to define the layout that will be inserted into the CardView ("SomeView") in a separate file and reference it with a custom attribute like this:
<xx.xyz.yy.customviews.ExpandableCardView
android:id="#+id/card_xyz"
android:layout_width="match_parent"
android:layout_height="match_parent"
custom_xxx:heading="SOME HEADING"
custom_xxx:expandedView="#layout/some_view"/>
I'll explain my rationale at the end, but let's look at an answer to your question as stated.
What you are probably seeing with your code is SomeView and expandable_card_view appearing all at once in the layout. This is because SomeView is implicitly inflated with the CardView and then expandable_card_view is added through an explicit inflation. Since working with layout XML files directly is difficult, we will let the implicit inflation occur such that the custom CardView just contains SomeView.
We will then remove SomeView from the layout, stash it, and insert expandable_card_view in its place. Once this is done, SomeView will be reinserted into the LinearLayout with the id card_body. All this has to be done after the completion of the initial layout. To get control after the initial layout is complete, we will use ViewTreeObserver.OnGlobalLayoutListener. Here is the updated code. (I have removed a few things to simplify the example.)
ExpandableCardView
public class ExpandableCardView extends CardView {
public ExpandableCardView(Context context) {
super(context);
}
public ExpandableCardView(Context context, AttributeSet attrs) {
super(context, attrs);
// Get control after layout is complete.
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Remove listener so it won't be called again
getViewTreeObserver().removeOnGlobalLayoutListener(this);
// Get the view we want to insert into the LinearLayut called "card_body" and
// remove it from the custom CardView.
View childView = getChildAt(0);
removeAllViews();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.expandable_card_view, ExpandableCardView.this, true);
// Insert the view into the LinearLayout.
((LinearLayout) findViewById(R.id.card_body)).addView(childView);
// And the rest of the stuff...
TextView headingTextView = findViewById(R.id.card_heading);
headingTextView.setText("THE HEADING");
// set collapse/expand click listener
ImageView collapseExpandButton = findViewById(R.id.collapse_expand_card_button);
collapseExpandButton.setOnClickListener((View v) -> toggleCardBodyVisibility());
}
});
}
private void toggleCardBodyVisibility() {
LinearLayout description = findViewById(R.id.card_body);
ImageView imageButton = findViewById(R.id.collapse_expand_card_button);
if (description.getVisibility() == View.GONE) {
description.setVisibility(View.VISIBLE);
imageButton.setImageResource(R.drawable.ic_arrow_up);
} else {
description.setVisibility(View.GONE);
imageButton.setImageResource(R.drawable.ic_arrow_down);
}
}
}
expandable_card_view.java
The CardView tag is changed to merge to avoid a CardView directly nested within a CardView.
<merge
android:id="#+id/expandable_card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="16dp"
android:animateLayoutChanges="true"
app:cardCornerRadius="4dp">
<RelativeLayout
android:id="#+id/card_header"
android:padding="12dp"
android:layout_width="match_parent"
android:layout_height="48dp"
android:orientation="horizontal" >
<TextView
android:id="#+id/card_heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="#color/colorPrimary"
android:layout_alignParentLeft="true"
android:text="HEADING"/>
<ImageView
android:id="#+id/collapse_expand_card_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
app:srcCompat="#drawable/ic_arrow_down"/>
</RelativeLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/card_body"
android:padding="12dp"
android:layout_marginTop="28dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:visibility="gone" >
</LinearLayout>
</merge>
activity_main.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.customcardview.ExpandableCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/imageView"
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/ic_android" />
<TextView
android:id="#+id/childView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Say my name."
android:textSize="12sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/imageView" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.example.customcardview.ExpandableCardView>
</LinearLayout>
So, why do I suggest that you use a custom attribute to include SomeView in the layout as I identified at the beginning? In the way outlined above, SomeView will always be inflated and there is some effort to switch the layout around although SomeView may never be shown. This would be expensive if you have a lot of custom CardViews in a RecyclerView for instance. By using a custom attribute to reference an external layout, you would only need to inflate SomeView when it is being shown and the code would be a lot simpler and easier to understand. Just my two cents and it may not really matter depending upon how you intend to use the custom view.

A Fragment's height (in a Viewpager) cannot be reduced

I have a tablayout with a viewpager that contains four fragments, the first 3 tabs( fragments) works fine. But the last fragment's height cannot be reduced . Its layout contains only an Imageview with fixed size height. but the fragment height is always greater than the imageview. I tried to put the imageview inside a frame layout and linearlayout, trying all height possibilities: match_parent, wrap_content and fixed size height but, it didn't work.
I tried to add maxHeight to the imageview with no luck.
I guessed that the problem may be with the view pager since it may be measuring other fragment's height and stuck with this height, but why other fragments is working fine.So I need your help. Thanks in advance
This is my last fragment layout:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="260dp"
android:maxHeight="260dp"
android:scaleType="centerCrop"
android:src="#drawable/gs350f"/>
And this is the viewpager layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:id="#+id/car_overview_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:background="#drawable/rounded_corner"
app:tabGravity="fill"
app:tabIndicatorColor="#android:color/transparent"
app:tabMaxWidth="0dp"
app:tabMode="fixed"
app:tabSelectedTextColor="#color/high" />
<com.myproject.ui.custom.NonSwipeableViewPager
android:id="#+id/overview_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/car_overview_tabs" />
And finally this is NonSwipeableViewPager class:
public class NonSwipeableViewPager extends ViewPager {
public NonSwipeableViewPager(Context context) {
super(context);
}
public NonSwipeableViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override public boolean onInterceptTouchEvent(MotionEvent event) {
// Never allow swiping to switch between pages
return false;
}
#Override public boolean onTouchEvent(MotionEvent event) {
// Never allow swiping to switch between pages
return false;
}
}

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);

ScrollView inside ViewPager, scrolls to middle automatically

I have a ViewPager that contains several instances of the same fragment, this fragment contains an article. The Article view hierarchy is quite simple, a Title, a Banner image, a subtitle and a body; everything but the title is wrapped in a scrollview.
The problem is, when you swipe to a new page, the fragment is presented with the Views at the top, and then it immediately scrolls to the middle of the container. (As a matter of fact it scrolls to the beginning of the TextView with id: article_content)
I have posted the layout at the bottom of the question.
Now, the ViewPager is set with a simple implementation of a FragmentStatePagerAdapter, here's the code:
class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
Bundle args;
int count;
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
this.count = 8;
}
#Override
public Fragment getItem(int position) {
ArticleFragment mPrimaryFragment = new ArticleFragment();
args = new Bundle();
args.putString(ArticleFragment.KEY_ARTICLE_URL, mCurArticleLink);
mPrimaryFragment.setArguments(args);
return mPrimaryFragment;
}
#Override
public int getCount() {
return count;
}
}
The Fragment itself is pretty simple too. First, I check during onCreate to see if we have the article cached, the I call on onCreateView
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.apk_article_view, null);
mTitle = (TextView) view.findViewById(R.id.article_title);
mBanner = (ImageView) view.findViewById(R.id.article_banner);
mSubTitle = (TextView) view.findViewById(R.id.article_subtitle);
mContent = (TextView) view.findViewById(R.id.article_content);
if (isArticleCached) {
Constants.logMessage("Article is cached, loading from database");
setApkArticleContent();
}
else {
Constants.logMessage("Article isn't cached, downloading");
HtmlHelper.setApkArticleContent(mContext, mUrl, mTitle, mSubTitle, mContent, mBanner);
setRefreshing(true);
}
return view;
}
It is worth noting that setApkArticleContent is a simple set of Texts, nothing fancy:
private void setApkArticleContent() {
mTitle.setText(Html.fromHtml(mCursor.getString(mCursor.getColumnIndex(DbOpenHelper.TITLE))));
mSubTitle.setText(Html.fromHtml(mCursor.getString(mCursor.getColumnIndex(DbOpenHelper.SUBTITLE))));
mContent.setText(Html.fromHtml(mCursor.getString(mCursor.getColumnIndex(DbOpenHelper.BODY))));
UrlImageViewHelper.setUrlDrawable(mBanner, mCursor.getString(mCursor.getColumnIndex(DbOpenHelper.BANNER)));
}
Also, please know that I did not have a pager before, the fragment was only loaded to an empty activity, and it worked without scrolling to the middle of the scrollview.
I am really not sure what is triggering the scroll, and yes, I know I can programatically set it to scroll back to the top after loading, but then again, that'd be two scroll movements when the fragment is loaded and it would be quite noticeable for the user.
Do you guys have any ideas why it would behave like this? Any ideas on how I can stop that unintentional scroll?
Thanks,
Below is the layout for the ArticleFragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/article_title"
style="#style/headerTextBoldNoUnderline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left|center_vertical"
android:text="" />
<ScrollView
android:layout_height="match_parent"
android:layout_width="match_parent"
>
<LinearLayout
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="match_parent"
>
<ImageView
android:id="#+id/article_banner"
android:layout_gravity="center_vertical|center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp" />
<TextView
android:id="#+id/article_subtitle"
style="#style/HeaderTextItalicNoUnderline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left|center_vertical" />
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="?android:attr/dividerVertical" />
<TextView
android:id="#+id/article_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:gravity="left|center_vertical"
android:padding="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textIsSelectable="true"
android:textSize="16sp" />
</LinearLayout>
</ScrollView>
</LinearLayout>
This is likely caused by android:textIsSelectable. You may try adding the following to the ScrollView:
android:focusable="true"
android:focusableInTouchMode="true"
android:descendantFocusability="beforeDescendants"
Add this attribute value android:descendantFocusability="blocksDescendants" to the child of ScrollView, according to this question: How to prevent a scrollview from scrolling to a webview after data is loaded?
I also had this problem. I solved it with setting android:focusable="true" and android:focusableInTouchMode="true" to the first element in the ScrollView's LinearLayout.
i try to add
android:focusable="true"
android:focusableInTouchMode="true"
android:descendantFocusability="beforeDescendants"
in the root viewGroup .
it's work's well.
like below.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
android:focusableInTouchMode="true"
android:descendantFocusability="beforeDescendants"
android:background="#color/normal_bg">
<ScrollView
android:id="#+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/rl_enter"
android:scrollbars="none">
</ScrollView>
</RelativeLayout>
I tried solution:
android:focusable=true
android:focusableInTouchMode=true
android:descendantFocusability=beforeDescendants
And i dont know why but I cant do this with my RelativeLayout which is parent view (this does not work).
I had to wrap my TextView inside FrameLayout and now everything is ok. Maybe this helps somebody.
<FrameLayout
android:id="#+id/detail_description_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/detail_parameters_container"
android:layout_centerInParent="true"
android:descendantFocusability="beforeDescendants"
android:focusable="true"
android:focusableInTouchMode="true" >
<TextView
android:id="#+id/detail_descritpion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="all"
android:padding="10dp"
android:textIsSelectable="true" />
</FrameLayout>
ScrollView inside ViewPager scrolls automatically but does not scrolls in middle..
Check out this.
pager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// TODO Auto-generated method stub
int x=iv.getLeft();
int y=iv.getTop();
scroll.scrollTo(x, y);
}
pager is ViewPager's object and scroll is ScrollView and iv is any View say TextView or ImageView.
When we change page in viewpager, scrollbar automatically scrolls but to the left. If you would have find solution to middle please let me know.. :)

Categories

Resources