I am trying to list items on my recycler view using the grid layout manager with two columns.
However, the first row of the grid seems to be having width issues.
When I scroll down until the first row is invisible, then scrolled back up, it will fix itself and show the correct width.
My items are retrieved from themoviedb API,
adapter.setMovieWrapper(body); // body contains data from the API
recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
recyclerView.setAdapter(adapter);
Here is the layout code for the grid item:
<?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="200dp"
android:padding="2dp"
android:orientation="vertical">
<ImageView
android:id="#+id/movie_thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerInside"
android:contentDescription="#string/movie_thumbnail_description"/>
<TextView
android:background="#drawable/bg_shade"
android:id="#+id/movie_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:textColor="#FFF"
android:ellipsize="end"
android:padding="8dp"/>
</FrameLayout>
Here is the adapter code:
public class MovieGridAdapter extends RecyclerView.Adapter<MovieGridItemViewHolder> {
MovieWrapper movieWrapper;
private Context context;
public MovieGridAdapter(Context context) {
this.context = context;
}
public MovieWrapper getMovieWrapper() {
return movieWrapper;
}
public void setMovieWrapper(MovieWrapper movieWrapper) {
this.movieWrapper = movieWrapper;
this.notifyDataSetChanged();
}
#Override
public MovieGridItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MovieGridItemViewHolder(
LayoutInflater.from(context)
.inflate(R.layout.movie_grid_item, parent, false)
);
}
#Override
public void onBindViewHolder(MovieGridItemViewHolder holder, int position) {
Movie movie = movieWrapper.getResults().get(position);
Uri uri = Uri.parse("https://image.tmdb.org/t/p/w185");
uri = uri.buildUpon().appendEncodedPath(movie.getPosterPath()).build();
Picasso.with(context)
.load(uri)
.into(holder.getThumbnail());
holder.getTitle().setText(movie.getTitle());
}
#Override
public int getItemCount() {
if (movieWrapper != null && movieWrapper.getResults() != null) {
return movieWrapper.getResults().size();
}
return 0;
}
}
and here is the activity that host the recycler view XML layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
tools:context="com.adityapurwa.popularmovies.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/movie_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>
Anyone know what is causing this issue and how to resolve it?
Thanks!
There seems to be an issue with my ConstraintLayout hosting the RecyclerView, I changed it to FrameLayout and the issue disappeared. Even though the layout and the RecyclerView both have its dimension set to match_parent, somehow it fails to detect the dimension.
Note: Changing the dimension to be absolute (e.g 100dp) also solved the problem, however it makes the layout unresponsive.
I have used FrameLayout around Recyclerview but did not work, I used this work around solution like this, for first 2 items i added dummy items and hidden the views, this worked for me , although this is not a perfect solution
Related
Here is what I am trying to do:
What is the simplest way to create rows that scroll together and are composed of variable sized clickable Views with the same height on Android
Basically create variable width columns that have the same width in every row. Also need to add, delete and add listeners. Seems like a fairly simple task, but I am finding Android's GUI library a lot harder to figure out than Java's and WPF's GUI library.
Here is my RecyclerView:
public class MainActivity extends AppCompatActivity {
private RecyclerView ampRecyclerView;
private RecyclerView.Adapter ampAdapter;
private RecyclerView.LayoutManager ampLayoutManager;
List<FunctionView> myDataset = new ArrayList<FunctionView>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setUpRecyclerView();
}
private void setUpRecyclerView() {
LinearLayout linearLayout = findViewById(R.id.main_ll);
linearLayout.setWillNotDraw(false);
ampRecyclerView = (RecyclerView) findViewById(R.id.AmpRecyclerView);
// use a linear layout manager
ampLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
ampRecyclerView.setLayoutManager(ampLayoutManager);
myDataset.add(new FunctionView(this));
myDataset.add(new FunctionView(this));
myDataset.add(new FunctionView(this));
// specify an adapter
ampAdapter = new MainActivityAdapter(myDataset, 1);
ampRecyclerView.setAdapter(ampAdapter);
}
}
My adapter
class MainActivityAdapter extends RecyclerView.Adapter<MainActivityAdapter.FunctionViewHolder> {
private List<FunctionView> views = new ArrayList<FunctionView>();
private List<LinearLayout> llViews = new ArrayList<>();
private int rows;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class FunctionViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public LinearLayout linearLayout;
public FunctionViewHolder(LinearLayout v) {
super(v);
linearLayout = v;
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MainActivityAdapter(List<FunctionView> myDataset, int rows) {
views = myDataset;
this.rows = rows;
}
// Create new views (invoked by the layout manager)
#Override
public MainActivityAdapter.FunctionViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
LinearLayout linearLayout = (LinearLayout) LayoutInflater.from(parent.getContext())
.inflate(R.layout.function_holder, parent, false);
llViews.add(linearLayout);
MainActivityAdapter.FunctionViewHolder vh = new MainActivityAdapter.FunctionViewHolder(linearLayout);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(MainActivityAdapter.FunctionViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
int width = 0;
for(FunctionView fv : views){
holder.linearLayout.addView(fv);
width += fv.getWidth();
}
holder.linearLayout.setMinimumWidth(width);
//TODO set the data
// holder.functionView = views.get(position);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return rows;
}
I know there are glaring design flaws. I am trying to get the scrolling working first, because every layout I try doesn't work how I'd like.
Here is the layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_ll"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/AmpRecyclerView"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
and the holder:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:isScrollContainer="true"
android:nestedScrollingEnabled="true"
>
</LinearLayout>
as you said there are glaring design flaws, but you need to change the android:layout_height to wrap_content and android:layout_width to match_Parent.
if your item's hight is match_parent then your inner layout's hight becomes the recyclerview's hight then there is no room for other items so there will be no scrolling.
also, put something like a textView in it to be able to see the items.
another note, the name is supposed to be item not holder. holder is related to ViewHolder which is a totally different thing. you can name it according to your activity for example if Your activity name is MainActivity so your activity layout is activity_main, then you can call the inner layouts item_main
I recommend watching a tutorial on youtube or read an article from medium or anywhere (you can simply just google android recyclerview example) to learn the basics.
<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:id="#+id/txt_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="SAMPLE"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:textSize="30sp"
/>
</LinearLayout>
I am using a recyclerview for displaying and broadcasting videos of users. However, when I scroll through the recycler view, I see my first view, which has my video gets recycled hence why it's not visible to other users. Is there a way I can make sure that the first view is not recycled so I dont have to worry about my video view getting resetrecycled every single time I scroll through my list?
Here's my code :
In my fragment...
...
<android.support.v7.widget.RecyclerView
android:id="#+id/videoList"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="invisible"
app:layout_constraintBottom_toTopOf="#+id/myButtonContainer"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/myoldContainer">
...
and the corresponding adapter...
public class GroupAdapter extends RecyclerView.Adapter<GroupAdapter.myViewHolder> {
private CopyOnWriteArrayList<Person> persons;
private Context mContext;
public GroupAdapter(#NonNull final CopyOnWriteArrayList<Person> persons , Context context) {
this.persons = persons;
this.mContext= context;
for (Person person : this.persons) {
//get names
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View layout = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_person, parent, false);
final MyViewHolder viewHolder = new MyViewHolder(layout);
return viewHolder;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final Person person = person.get(position);
final Participant participant = person.getParticipant();
if (person.getName() != null) {
holder.personName.setText(person.getName());
}
if (person.getImage() != null) {
holder.personImage.setImageBitmap(person.getImage());
} else {
holder.personImage.setImageResource(R.drawable.default_profile);
}
holder.personImage.setVisibility(View.INVISIBLE);
holder.personImage.setVisibility(View.VISIBLE);
final VideoView videoView;
if (participant.isMe) {
videoView = participant.videoStreamer.videoView;
} else {
videoView = participant.videoPlayer.videoView;
}
if (holder.personVideo.getChildCount() != 0) {
holder.personVideo.removeAllViews();
}
if (videoView.getParent() != null) {
ViewGroup parent = (ViewGroup) videoView.getParent();
parent.removeView(videoView);
}
holder.personVideo.addView(videoView, myViewHolder.videoLayoutParams);
if (person.isVideoPaused()) {
holder.personVideo.setVisibility(View.INVISIBLE);
holder.personImage.setVisibility(View.VISIBLE);
} else {
holder.personVideo.setVisibility(View.VISIBLE);
holder.personImage.setVisibility(View.INVISIBLE);
}
}
#Override
public int getItemCount() {
return persons.size();
}
public static final class MyViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.personVideo)
public ViewGroup personVideo;
#BindView(R.id.personImage)
public ImageView personImage;
#BindView(R.id.personName)
public TextView personName;
protected static FrameLayout.LayoutParams videoLayoutParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
);
public MyViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
Here's how I am setting it in my fragment:
LinearLayoutManager manager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
videoAdapter = new VideoAdapter(myHelper.getPeople(), getContext());
videoList.setLayoutManager(manager);
videoList.setAdapter(videoAdapter);
item_person:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="0dp"
android:background="#drawable/person_border"
xmlns:app="http://schemas.android.com/apk/res-auto">
<RelativeLayout
android:id="#+id/personContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent">
<FrameLayout
android:id="#+id/personVideo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/black" />
<ImageView
android:id="#+id/personImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/black"
android:src="#drawable/default_profile" />
<TextView
android:id="#+id/personName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#AA555555"
android:gravity="center_horizontal|bottom"
android:textColor="#color/green"
android:textSize="12sp"
android:lines="1"
android:ellipsize="end"
tools:text="androiduser#gmail.com"
android:layout_alignParentBottom="true" />
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
fragment with recycle view: xml
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<RelativeLayout>
....
</RelativeLayout>
<include containers>...</include>
...
<android.support.v7.widget.RecyclerView
android:id="#+id/personList"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="invisible"
>
</android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>
I don't think that "not recycling" the video view will actually stop it from being destroyed when you scroll. It's not the recycling is the problem but the view unbinding.
I think such complex component such as VideoView should not be inside the RecyclerView at all.. You can try adding it as a static content on top, which most likely will solve the issue. You can use a NestedScrollView for that. Take a look here: Recyclerview inside scrollview- How to scroll whole content?
If you still think you want to keep it in the RecyclerView and disable the recycling, do the following.
Create a separate view type for your video view items. Here is an example:
https://stackoverflow.com/a/26573338/3086818. Treat your video view item as a header view from the example.
Once you do this, there is a RecycledViewPool class which manages the recycling of items inside a RecyclerView. You can tell it which views to recycle and which not. By default it recycles all views. To disable recycling for your video view items use your new view type like this:
recyclerView.getRecycledViewPool().setMaxRecycledViews(TYPE_VIDEO_VIEW, 0);
where TYPE_VIDEO_VIEW is the new type that you created using the previous example. The number 0 tells the RecyclerView how many items to recycle - in this case it's 0, meaning "do not recycle". More info about this here: https://stackoverflow.com/a/36313437/3086818.
I hope this helps.. Good luck!
The answer is yes, you can actually do that.
Since you said "The first item" so you simply add a check.
if(position == 0)
{
holder.setIsRecyclable(false); //This line prevents the row from recycling
}
I inflate another layout to appear below some view in my current layout.
This is done like this:
LayoutInflater vi = (LayoutInflater) getActivity().getApplicationContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rootView = vi.inflate(R.layout.horizontal_scroll_view, null);
horizontalScrollView = (HorizontalScrollView) rootView.findViewById(R.id.hsv_suggestions_scroll_view);
LinearLayout suggestionsContainer = (LinearLayout) horizontalScrollView.findViewById(R.id.ll_suggestions_container);
and I can confirm that it appears in the right place since I add some Views in it after a while and they all appear.
The layout I inflate is :
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/hsv_suggestions_scroll_view"
android:scrollbars="none" android:layout_gravity="center_horizontal"
android:paddingTop="16dp" android:fillViewport="true"
android:layout_width="match_parent" android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/ll_suggestions_container"
android:gravity="center_horizontal" android:orientation="horizontal"
android:layout_width="wrap_content" android:layout_height="wrap_content">
</LinearLayout>
just a HorizontalScrollView with a LinearLayout as a child.
The Views I add later are all of them TextViews.
Now after a user action (write some text on an editText) I'm trying to scroll to that View and highlight it. Highlight works. What does not work is scroll.
I have tried :
horizontalScrollView.postDelayed(new Runnable() {
#Override
public void run() {
horizontalScrollView.smoothScrollTo(scrollTo, 0);
}
}, 300);
where variable scrollTo is what I get when I apply getLeft() to the View I wanna scroll to. I can confirm that it takes various values.
Anyone can help me with that ?
Got a similar issue.
I guess the root cause is that the UI was not yet rendered at that moment, causing the scroll not to work properly.
I just had to wrap my call to smoothScrollTo into .post as follows:
mScrollView.post(new Runnable() {
#Override
public void run() {
mScrollView.smoothScrollTo(xxx, yyy);
}
});
Notice that I do a post directly on the scrollView to make sure it is executed after it is being rendered. For example, doing a getActivity().runOnUIThread() would not work in my case.
Switch to using a RecyclerView with a LinearLayoutManager with orientation set to Horizontal. Like this:
scroll_view.xml
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/scrollView"
android:scrollbars="none"
android:layout_gravity="center_horizontal"
android:paddingTop="16dp"
android:fillViewport="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
android:orientation="horizontal">
<LinearLayout
android:id="#+id/scrollContainer"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</android.support.v7.widget.RecyclerView>
Note: the layoutManager tag is required here for the layout to inflate. After it's inflated, we're going to set it programatically as well because otherwise we'll get an exception because the RecyclerView basically disposes of it before it's done with it.
OptionAdapter.java
public class OptionAdapter extends RecyclerView.Adapter<OptionAdapter.OptionHolder> {
private Context context;
private LayoutInflater inflater;
private ArrayList<String> options;
public OptionAdapter(Context context) {
this.context = context;
this.inflater = LayoutInflater.from(context);
options = new ArrayList<>();
}
#Override
public OptionHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new OptionHolder(inflater.inflate(R.layout.textview, parent, false));
}
#Override
public void onBindViewHolder(OptionHolder holder, int position) {
String option = options.get(position);
((TextView) holder.itemView).setText(option);
}
#Override
public int getItemCount() {
return options != null ? options.size() : 0;
}
public ArrayList<String> getOptions() {
return options;
}
public void addOption(String option, Integer index) {
if (index != null && index <= options.size()) {
options.add(index, option);
} else {
options.add(option);
}
notifyDataSetChanged();
}
public class OptionHolder extends RecyclerView.ViewHolder {
public OptionHolder(View itemView) {
super(itemView);
}
}
}
text_view.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
Setting everything up
final LayoutInflater inflater = LayoutInflater.from(this);
final RecyclerView scrollView = (RecyclerView) inflater.inflate(R.layout.scroll_view, container, false);
scrollView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
final OptionAdapter adapter = new OptionAdapter(this);
scrollView.setAdapter(adapter);
container here is whatever view is going to be holding the RecyclerView. In my code, I've got it in a LinearLayout.
Adding a view to the list
adapter.addOption("The Added One", null);
Or if you want to add it to a specific position in the list.
adapter.addOption("The Added One", position);
Scrolling to a specific position
scrollView.smoothScrollToPosition(position);
Scrolling to a specific item in the list
scrollView.smoothScrollToPosition(adapter.getOptions().indexOf("ItemText"));
Hope it works for you!
Instead of smoothScrollTo(), try using scrollTo().
Make sure that your getLeft() is really returning a value > 0;
This question has been asked many times but it just doesn't seem to have a solution that fits my requirement.
I have a Recipe that has instructions. Each instruction is ordered in a RecyclerView. The order must be from top to bottom. It cannot be reversed like many answers to this question.
I have an Add button that when clicked a new item is added at the bottom of the list. I need to scroll to the new item after the insert. So I employed the following code in the button's onClick event:
mRecyclerAdapter.addItem(newInstruction);
mRecyclerView.smoothScrollToPosition(mRecyclerAdapter.getItemCount() - 1);
This scrolls to the second last item because in the adapter's addItem() method I have this:
mItems.add(item);
notifyItemInserted(getItemCount() - 1);
I don't know the details of the notifyItemInserted() call, but it appears to be on a separate thread, which means that smoothScrollToPosition() is called before the views have been laid out after the insertion. Hence, it only scrolls to the second last item.
I have tried looking for a "callback" function along the lines of onItemInserted but I did not have any luck. eg, no answer Here.
How do I solve this problem?
EDIT START
I've simplified the code in a new project in hope to find the problem, and found out what the problem is. It appears to be a problem caused by CoordinatorLayout/AppBarLayout when scrolling flag is enabled. It seems as though the scrolling did not take into consideration the extra scrolling space added by the AppBarLayout.
In the sample codes below, if you disable the scrolling flag for the app bar, scrolling to the bottom of the screen works perfectly as intended. However, if you enable the AppBarLayout scrolling behavior, and change its height, you'll notice that scrolling to the bottom is off by the given height.
I need the scrolling behavior, so how do I make them play nicely with each other? Please note that I've changed the question to reflect the problem.
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
public static final int ITEMS_COUNT = 50;
private FloatingActionButton mFloatingActionButton;
private RecyclerView mRecyclerView;
private RecyclerAdapter mRecyclerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFloatingActionButton = (FloatingActionButton) findViewById(R.id.fabButton);
mFloatingActionButton.setOnClickListener(this);
setupRecyclerView();
}
private void setupRecyclerView() {
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerAdapter = new RecyclerAdapter(createItemList());
mRecyclerView.setAdapter(mRecyclerAdapter);
}
private List<String> createItemList() {
List<String> itemList = new ArrayList<>();
for (int i = 0; i < ITEMS_COUNT; i++) {
itemList.add("Item " + i);
}
return itemList;
}
#Override
public void onClick(View v) {
mRecyclerView.scrollToPosition(49);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- changing the height affects the scrolling distance -->
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Header"
app:layout_scrollFlags="scroll|enterAlways"/> <!-- this is the problem -->
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fabButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="20dp"/>
</android.support.design.widget.CoordinatorLayout>
RecyclerAdapter.java
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<String> mItemList;
public RecyclerAdapter(List<String> itemList) {
mItemList = itemList;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
View view = LayoutInflater.from(context).inflate(R.layout.recycler_item, parent, false);
return RecyclerItemViewHolder.newInstance(view);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
RecyclerItemViewHolder holder = (RecyclerItemViewHolder) viewHolder;
String itemText = mItemList.get(position);
holder.setItemText(itemText);
}
#Override
public int getItemCount() {
return mItemList == null ? 0 : mItemList.size();
}
}
recycler_item.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
card_view:cardCornerRadius="4dp">
<TextView
android:id="#+id/itemTextView"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:padding="8dp"
style="#style/Base.TextAppearance.AppCompat.Body2"/>
</android.support.v7.widget.CardView>
EDIT END
Doing a bit more research I found this question/answer which has a workaround to this problem. Although not ideal, it will have to do for now. Though the real solution shouldn't have to collapse the AppBarLayout, the scrolling should take into consideration the extra scrolling space needed. Perhaps this is just a bug that is overlooked.
In your addItem() method change this
notifyItemInserted(getItemCount() - 1);
to
notifyItemInserted(mItems.size() - 1);
This will solve the problem!
add this after adding item
notifyItemRangeChanged(0, items.size());
Have you tried
mRecyclerView.smoothScrollToPosition(mRecyclerView.getAdapter().getItemCount());
You can also put it in a runnable like this to make sure item has added before you scroll to it.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mRecyclerView.smoothScrollToPosition(mRecyclerView.getAdapter().getItemCount());
}
}, 1000);
Before you add list to adapter in Recycleview do some sorting by using Collection method according to your need,use this code may help you
Comparator<Long> comparator = Collections.reverseOrder();
Collections.sort(arrayList, comparator);
Goodd day.I have simple recycler view with simplest dummy datas for test purpose,thus i have an weird issue to which the google did not find any solution or even an issue at all.On first launch the view is all good but as soon as i start to scrool,the child items are being as far from each other as no one can image...Really very and very far.But the issue is that the actual child items layout parameters are correct,only issue is that i dont know why RecyclerView decides to have each item heaps far away from each other.Please can you give me an help?Posting full code of my RecyclerView.
The view for recyclerView
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="com.ink.activities.HomeActivity"
tools:showIn="#layout/app_bar_home">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical" />
</RelativeLayout>
The Adapter.
public class FeedAdapter extends RecyclerView.Adapter<FeedAdapter.ViewHolder> {
private List<FeedModel> feedList;
private Context mContext;
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView title, content;
public ViewHolder(View view) {
super(view);
title = (TextView) view.findViewById(R.id.feedTitle);
content = (TextView) view.findViewById(R.id.feedContent);
}
}
public FeedAdapter(List<FeedModel> feedList, Context context) {
mContext = context;
this.feedList = feedList;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.feed_single_view, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
FeedModel feedModel = feedList.get(position);
holder.title.setText(feedModel.getTitle());
holder.content.setText(feedModel.getContent());
// animate(holder);
}
public void animate(RecyclerView.ViewHolder viewHolder) {
final Animation animAnticipateOvershoot = AnimationUtils.loadAnimation(mContext, R.anim.bounce_interpolator);
viewHolder.itemView.setAnimation(animAnticipateOvershoot);
}
#Override
public int getItemCount() {
return feedList.size();
}
}
I guess you won`t need holder as no view initiated with it.
The single child item view of RecyclerView adapter.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardCornerRadius="5dp"
app:cardElevation="10dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/feedTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:fontFamily="#string/appFont"
android:text="loading...."
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:id="#+id/feedContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/feedTitle"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:layout_marginTop="10dp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
The initiation of actual parameters.
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mAdapter = new FeedAdapter(mFeedModelArrayList, this);
RecyclerView.ItemAnimator itemAnimator = new DefaultItemAnimator();
itemAnimator.setAddDuration(500);
itemAnimator.setRemoveDuration(500);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setItemAnimator(itemAnimator);
This code is as simple as it can get and it is important to mention that i am initiation all this inside the default NAVIGATION DRAWER ACTIVITY of android studio (the default templae inside content_main layout).So plaese can you give me any hint about the issue?
You're using
android:layout_width="match_parent"
android:layout_height="match_parent"
on your child item views. As of Support Library 23.2:
The RecyclerView widget provides an advanced and flexible base for creating lists and grids as well as supporting animations. This release brings an exciting new feature to the LayoutManager API: auto-measurement! This allows a RecyclerView to size itself based on the size of its contents. This means that previously unavailable scenarios, such as using WRAP_CONTENT for a dimension of the RecyclerView, are now possible. You’ll find all built in LayoutManagers now support auto-measurement.
Due to this change, make sure to double check the layout parameters of your item views: previously ignored layout parameters (such as MATCH_PARENT in the scroll direction) will now be fully respected.
Change your layout_height to wrap_content if you only want your items to be as large as needed. match_parent means they will be as large as the screen.