Custom GridLayoutManager - android

I am thinking of a way to implement a view using RecyclerView like this image where the header row is on the left side and the content under that header on the right. Any ideas or pointers on how to go about this.

If someone else gets here looking for an answer/ solution, here's what I did. I used default GridLayoutManager using child RecyclerView in the parent RecyclerView. Parent RV is using GridLayoutManager while child RV uses LinearLayoutManager. For parent RV, using multiple viewType, position 0 or position %2 == 0 returns HEADER_TYPE while the rest returns CARD_TYPE. Using this
mGridLayoutManager = GridLayoutManager(activity, 3)
mGridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when (enquiryAdapter.getItemViewType(position)) {
HEADER_TYPE -> 1
CARD_TYPE -> 2
else -> -1
}
}
}
recyclerView.layoutManager = mGridLayoutManager
I changed the spanSize. For position that returns CARD_TYPE, I inflate another RV which takes list to layout the content view. I am not sure this is the best solution but that seems to be working for me. I will gladly accept any solution working better/simpler than this.

Related

Merge adapter with different layout managers

I am using MergeAdapter to combine 2 adapters It works fine .
Problem is for 1 adapter I need LinearLayoutManager with single column and for second adapter I need GridLayoutManager to show items in 2 columns . Is there any way to do it ?
I found a working solution(added below) by setting it to GridLayoutManager and span size based on position . I just want to know is there a better way to do this .
Set GridLayoutManager to RecyclerView and span size based on position .
val layoutManager = GridLayoutManager(requireContext(), 2)
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
if(position==0){
return 2 //no of colums it show occupy
}
return 1
}
}
recyclerView.layoutManager = layoutManager
In this case if position in mergeadapter is 0 then the only 1 column will be show come else 2 columns will be shown .

How to arrange multiple images horizontally on single activity

I am new to android i have developed an application in which i have to display images horizontal view on single activity i have done using staggered recycleview but am getting like this.
But i want to design like this as part of the activity.
You can easily do that with GridLayoutManager. Use SpanSizeLookup to control your row/column . For your case you have to use HORIZONTAL orientation. SpanSizeLookup will help you to control your rows in each column in HORIZONTAL GridLayoutManager.
GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), NUM_OF_ROW, LinearLayoutManager.HORIZONTAL, reverseOrder);
GridLayoutManager.SpanSizeLookup spanSizeLookup = new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
// for position 0 use only one row. for this position it will take all rows
if (position == 0) {
return NUM_OF_ROW;
}
return 1;
}
};
Here is blog post about different Layout Manager implementation.
I have uploaded a repo on Github about different LayoutManager usage like LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager and some advance RecyclerView usage like swipe, Drag and Drop. You can also check that
here is what i use for arranging items horizontally provided you're using RecyclerView:
int numberOfColumns = 3; //edit as you want pls
final GridLayoutManager gridLayoutManager = new GridLayoutManager(this.getContext(), numberOfColumns);
recyclerView.setLayoutManager(gridLayoutManager);
recyclerView.setAdapter(adapter);
I might be off your solution, perdon if so, but pasting some of your code might help better.

RecyclerView scrolls to bottom when data is loaded

I have some weird issue with RecyclerView since I changed my app to load the data from Room database.
I have a screen (a Fragment) which displays a Profile for a user. Basically it's a GridLayout with the first item (row) displaying some info about the user and then for each row I'm displaying three images.
The code for the RecyclerView is :
private void initializeRecyclerView() {
if (listAdapter == null) {
listAdapter = new PhotosListAdapter(getActivity(), userProfileAccountId, false);
}
rvPhotosList.setNestedScrollingEnabled(true);
rvPhotosList.getItemAnimator().setChangeDuration(0);
GridLayoutManager layoutManager = new GridLayoutManager(getContext(), ProfileFragment.GRID_COLUMNS,
LinearLayoutManager.VERTICAL, false);
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
// 3 spans for Header, 1 for photos
return listAdapter.isHeader(position) ? 3 : 1;
}
});
rvPhotosList.setLayoutManager(layoutManager);
int spacingInPixels = 1dp;
rvPhotosList.addItemDecoration(new PaddingItemDecoration(GRID_COLUMNS, spacingInPixels, true));
rvPhotosList.setAdapter(listAdapter);
}
and this is how I load data :
compositeDisposable.add(
repository.getAccount(accountId)
.doOnNext(account -> {
if (account != null && view != null) {
view.showCover(account.getCover());
view.showAccount(account);
}
})
.flatMap(s -> repository.getUserPosts(accountId))
.subscribe(posts -> {
if (view != null) {
view.showPosts(posts);
}
}, throwable -> {}));
Both calls return a Flowable, either a Flowable<Account> or a Flowable<Post> where Account and Post are Room's Entity classes.
The two methods showAccount() and showPosts() pass the previously mentioned entity classes to the adapter.
The problem is this : the moment the data are loaded it scrolls to the bottom of the screen (which is also the bottom of the RecyclerView).
I'm not sure why this happens. Is it because of Room's Flowable type?Cause previously when I was fetching the data from network and passing them to the adapter I didn't have this problem.
Edit : I'm using version 25.3.1 for RecyclerView
Edit 2 : This is how I'm updating my adapter class with Account and Post data :
public void addPhotos(List<Post> posts) {
dataset.addAll(posts);
notifyDataSetChanged();
}
public void addProfileItem(Account account) {
this.account = account;
notifyItemInserted(0);
}
Did you try this? Setting descendantFocusability property of parent RecyclerView to blocksDescendants.
What this will do is, it will no more focus on your dynamically loaded child views inside recycler view, as a result, the automatic scroll will not take place.
android:descendantFocusability="blocksDescendants"
Note: The property descendantFocusability can be used with other views as well like FrameLayout, RelativeLayout, etc. So if you set this property to your parent/root layout, it will no more scroll to bottom.
If you have some initial items, that should be at the bottom after new items loaded, then you need to give them new ids, if you don't want to recycler scrolls to the bottom.
It happens because the recycler remember that this items was visible to the user before update and after update he restore his state so that initial items at the bottom will be displayed
In my case this line was culprit,
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, SPAN_COUNT, GridLayoutManager.VERTICAL, true);
Specifically , last parameter which is setting reverseLayout attribute to true. Make it false and RecyclerView doesn't scroll to bottom of screen.

Android RecyclerView Adapter: notifyItemInserted and notifyItemMoved at index 0 not working

I have a RecyclerView with a horizontal linear layout manager declared like this:
RecyclerView graph = (RecyclerView) findViewById(R.id.graph);
RecyclerView.LayoutManager classManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
graph.setLayoutManager(classManager);
graph.addItemDecoration(new ComponentDecorator(this)); //Just sets a margin around each item
I have a method which inserts a placeholder view into the RecyclerView like this:
private void insertPlaceholder(int index) {
int placeholderIndex = getIndexOfPlaceholder(); //returns index of existing placeholder, -1 if none
//No need to do anything
if(placeholderIndex == index)
return;
if(placeholderIndex == -1) {
ClassGraphItem placeholder = new ClassGraphItem();
placeholder.setType(ClassGraphItem.PLACEHOLDER);
mItems.add(index, placeholder);
Print.log("notify item inserted at index", index);
notifyItemInserted(index);
}
else {
ClassGraphItem placeholder = mItems.get(placeholderIndex);
mItems.remove(placeholderIndex);
mItems.add(index, placeholder);
notifyItemMoved(placeholderIndex, index);
}
}
The placeholder is just an invisible view which simulates a space opening between two existing views:
private class PlaceholderViewHolder extends RecyclerView.ViewHolder {
public PlaceholderViewHolder(View itemView) {
super(itemView);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(mComponentWidth, 1);
itemView.setLayoutParams(params);
itemView.setVisibility(View.INVISIBLE);
}
}
When the inserted index is > 0, it works perfectly. However at index 0, either inserting a placeholder, or moving an existing placeholder to the 0 index does not work, specifically the RecyclerView doesn't animate to show the new item inserted at index 0. If I used notifyDataSetChanged() it does work. however that doesn't animate and isn't the effect I'm looking for. This seems like a bug to me, but I wanted to make sure there wasn't something else that was causing this issue.
I'm on the latest version of the recyclerview support library (24.2.1). Thanks!
I removed recycler.setHasFixedSize(true); and now it works. I have no idea why this would be related though.
This may happen because of setting recycler.setHasStableIds(true) and then using item's position to assign the getItemId in the adapter.
This will just update that one position and other items won't be shifted as they have should.

How to use RecyclerView.scrollToPosition() to move the position to the top of current view?

The RecyclerView.scrollToPosition() is extremely strange.
for example, supposed a RecyclerView named "rv".
if now item 10 is under the current RecyclerView, call rv.scrollToPosition(10), the RecyclerView will scroll item 10 to bottom.
if now item 10 is in the current RecyclerView, call rv.scrollToPosition(10), there won't be any scrolling, nothing will be done.
if now item 10 is on the top of the current RecyclerView, call rv.scrollToPosition(10), the RecyclerView will scroll item 10 to top.
To help understanding, look at this picture
But what i need is that, whenever i call it, the RecyclerView will scroll the supposed position to the top of current view just like case 3. How to do that?
If I understand the question, you want to scroll to a specific position but that position is the adapter's position and not the RecyclerView's item position.
You can only achieve this through the LayoutManager.
Do something like:
rv.getLayoutManager().scrollToPosition(youPositionInTheAdapter).
Below link might solve your problem:
https://stackoverflow.com/a/43505830/4849554
Just create a SmoothScroller with the preference SNAP_TO_START:
RecyclerView.SmoothScroller smoothScroller = new
LinearSmoothScroller(context) {
#Override protected int getVerticalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_START;
}
};
Now you set the position where you want to scroll to:
smoothScroller.setTargetPosition(position);
And pass that SmoothScroller to the LayoutManager:
layoutManager.startSmoothScroll(smoothScroller);
If you want to scroll to a specific position and that position is the adapter's position, then you can use StaggeredGridLayoutManager scrollToPosition
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.VERTICAL);
staggeredGridLayoutManager.scrollToPosition(10);
recyclerView.setLayoutManager(staggeredGridLayoutManager);
This is the Kotlin code snippet but you can just get the point for scrolling to the item by position properly. The point is to declare the member variable for the layout manager and use its method to scroll.
lateinit var layoutManager: LinearLayoutManager
fun setupView() {
...
layoutManager = LinearLayoutManager(applicationContext)
mainRecyclerView.layoutManager = layoutManager
...
}
fun moveToPosition(position: Int) {
layoutManager.scrollToPositionWithOffset(position, 0)
}
try recycler_view.smoothScrollBy(0, 100); here 0 represents x-coordinate and 100 represents y-coordinate I used this in vertical recyclerView where I wanted to scroll to next position in the vertical list and for coming to the previous position I simply replaced 100 with -100

Categories

Resources