I'm trying to create a to-do list just like "google keep" android app.
Basically, I need a scrolled layout with 2 lists: one of the checked items, and one of the unchecked ones.
The unchecked list should have an option to drag & drop the items in order to change their position.
My only problem is that I can't use width=wrap_content in recyclerview, and the layoutManager implementation that I found make the app crash when I try to drag & drop.
For the drag & drop I use this library.
My layout:
<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:fillViewport="true"
tools:context="views.fragments.ItemsFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<AutoCompleteTextView
android:id="#+id/fragment_items_atv_name"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content" />
<Button
android:id="#+id/fragment_items_btn_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="#string/add" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/fragment_items_lv_unchecked_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v7.widget.RecyclerView
android:id="#+id/fragment_items_lv_checked_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/fragment_items_tv_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Empty list"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
</ScrollView>
The layout manager I use
public class MyLinearLayoutManager extends LinearLayoutManager {
public MyLinearLayoutManager(Context context) {
super(context, VERTICAL, false);
}
private int[] mMeasuredDimension = new int[2];
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
height = height + mMeasuredDimension[1];
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
}
}
You can use ItemTouchHelper in supportv7, and implement simplecallback,like this:
mSimpleCallback = new ItemTouchHelper.SimpleCallback(
ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT|ItemTouchHelper.UP|ItemTouchHelper.DOWN, 0) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition();//得到拖动ViewHolder的position
int toPosition = target.getAdapterPosition();//得到目标ViewHolder的position
List<NHUser.DescriptionEntity> datas = mItemAdapter.getData();
if (fromPosition < toPosition) {
//分别把中间所有的item的位置重新交换
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(datas, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(datas, i, i - 1);
}
}
mItemAdapter.notifyItemMoved(fromPosition, toPosition);
return true;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
};
Related
In my activity , I want to put a slideShow above my page and then put recycleview after that, to do so ,I'v written this code in my layout:
<?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="match_parent"
android:background="#fff"
android:orientation="vertical"
android:padding="6dp">
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
card_view:cardBackgroundColor="#fff"
card_view:cardCornerRadius="3dp"
card_view:cardElevation="0dp">
<cn.trinea.android.view.autoscrollviewpager.AutoScrollViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="250dp" />
</android.support.v7.widget.CardView>
<android.support.v7.widget.RecyclerView
android:id="#+id/recycle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
the problem with this code is that when I run the activity , only the recycleview scrolls , I don't want that, I want if I scroll down ,the whole page scrolls down not just the recycle .
How can I make the whole page scroll ?
Three steps to do this:
Replace your scrollview with NestedScrollView with layout behaviour set to app:layout_behavior="#string/appbar_scrolling_view_behavior"
Keep all your widgets under a linear/relative layout(because nested scroll view accepts only one child)
3.create CustomLinearLayoutManager.java with this code:
public class CustomLinearLayoutManager extends LinearLayoutManager {
public CustomLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
private int[] mMeasuredDimension = new int[2];
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
height = height + mMeasuredDimension[1];
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
}
}
set adapter to your recyclerview like this:
CustomLinearLayoutManager customLinearLayoutManager = new CustomLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
updatesRecyclerView.setHasFixedSize(true);
updatesRecyclerView.setLayoutManager(customLinearLayoutManager);
updatesRecyclerView.setAdapter(yourAdapter);
updatesRecyclerView.setNestedScrollingEnabled(false);
That's it , it works.
I have a RecyclerView named DogRecyclerView. I have another RecyclerView called FoodRecyclerView. FoodRecyclerView happens to be one of the many layouts of DogRecyclerView (i.e. one of the ViewHolders). DogRecyclerView is a list. FoodRecyclerView is actually as below. The problem is that the view below is not wrapping the content but rather it is taking more height than it should. How might I fix this?
<?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:layout_width="match_parent"
android:background="#f00"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="This is a Label that I am testing right now"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/food_grid"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
So as you can see I lied a little. The nested view is actually a LinearLayout that wraps a RecyclerView and a TextView. Deee-tails...
I left out something oh so important: my LayoutManager is as below:
public class NestedGridLayoutManager extends GridLayoutManager {
public NestedGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public NestedGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
}
public NestedGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
}
private int[] mMeasuredDimension = new int[2];
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
height = height + mMeasuredDimension[1];
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
}
}
Support for WRAP_CONTENT and generally auto-measuring the size of the RecyclerView based on its content is one of the headlines features of the Support Library 23.2.0 release. As long as you are using an included LayoutManager, you should get this behavior for free.
I'm using a RecyclerView that contains CardViews with a TextView and a ImageView (Every card represents a city). I also have a onClickListener on every card that leads me to a list of museum in the city. (The RecyclerView is populated by an ArrayList).
The list is a RecyclerView composed by the same Cardview that scrolls vertically.
When a city has only one museum, how can I display the unique CardView at the center of the screen?
This is the activity xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.bebbo203.mymuseum.MuseumActivity">
<android.support.v7.widget.RecyclerView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/recyclerViewMuseum"
android:scrollbars="vertical"
android:scrollIndicators="none"
android:gravity="center_horizontal"
/>
</RelativeLayout>
And this is the RecyclerView xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
xmlns:card_view="http://schemas.android.com/apk/res-auto">
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="150dp"
android:id="#+id/cardView"
card_view:cardCornerRadius="2dp"
card_view:cardUseCompatPadding="true"
android:gravity="center_horizontal"
android:animateLayoutChanges="true"
>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="150dp"
android:id="#+id/imageViewList"
android:layout_gravity="center_horizontal|top"
android:adjustViewBounds="true"
android:scaleType="centerCrop"/>
<TextView
android:id="#+id/textViewList"
android:layout_width="match_parent"
android:layout_height="150dp"
android:textSize="40sp"
android:textIsSelectable="false"
android:textAlignment="center"
android:gravity="fill"
android:textStyle="bold"
android:layout_weight="1"
android:layout_gravity="center_horizontal|top"/>
</FrameLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>`
Thanks for helping.
So this is the MainActivity. A list of Cities. All is good here.
When I click on Parigi that has only one museum I wanted to show the single cardview at the center of the screen
(And if it's possible i would like to make the cardview starting from the center of the screen, not from the top. Like if the central cardview is always at the center when I open the activity. For example translating NationalGallery at the center mantaining the order of the other)
I've implemented simple HelloWorld app, which shows list of cities and based on how many museums it has - shows full-sized city-card or the centered, wrapped version of it.
(Yes, I'm not exactly good at arts :-) )
Here's how I did it.
TL;DR:
The crucial part is ItemDecoration: set proper items offset and you'll get what you need; Here's how I've done it:
RecyclerView recyclerViewMuseum = (RecyclerView)findViewById(R.id.recyclerViewMuseum);
recyclerViewMuseum.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
recyclerViewMuseum.setAdapter(adapter);
recyclerViewMuseum.addItemDecoration(new RecyclerView.ItemDecoration() {
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (view instanceof CityWithOneMuseumCardView) {
int totalWidth = parent.getWidth();
int cardWidth = getResources().getDimensionPixelOffset(R.dimen.small_card_width);
int sidePadding = (totalWidth - cardWidth) / 2;
sidePadding = Math.max(0, sidePadding);
outRect.set(sidePadding, 0, sidePadding, 0);
}
}
});
Here's my model - City and Museum classes:
public class Museum {
public String title;
public Museum(String title) {
this.title = title;
}
}
public class City {
public String title;
public int imageRes;
public List<Museum> museums = new ArrayList<>();
public City(String title, int imageRes) {
this.title = title;
this.imageRes = imageRes;
}
}
Then Views: CityWithManyMuseumsCardView and CityWithOneMuseumCardView. Both of them are using helper-interface IItemDisplayer.
public class CityWithOneMuseumCardView extends CardView implements IItemDisplayer<City> {
public CityWithOneMuseumCardView(Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.one_museum_layout, this);
}
#Override
public void displayItem(City city) {
TextView cityTitleTextView = (TextView)findViewById(R.id.cityTitleTextView);
cityTitleTextView.setText(city.title);
}
}
public class CityWithManyMuseumsCardView extends CardView implements IItemDisplayer<City> {
public CityWithManyMuseumsCardView(Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.many_museums_layout, this);
}
#Override
public void displayItem(City city) {
ImageView cityBackgroundImageView = (ImageView)findViewById(R.id.cityBackgroundImageView);
cityBackgroundImageView.setImageResource(city.imageRes);
TextView cityTitleTextView = (TextView)findViewById(R.id.cityTitleTextView);
cityTitleTextView.setText(city.title);
TextView cityNumberOrMuseumsTextView = (TextView)findViewById(R.id.cityNumberOrMuseumsTextView);
cityNumberOrMuseumsTextView.setText(String.valueOf(city.museums.size()));
}
}
public interface IItemDisplayer<TItem> {
public void displayItem(TItem item);
}
And their layouts :
<!-- One Museum card -->
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#BB2050AB"
android:layout_width="#dimen/small_card_width"
android:layout_height="200dp">
<TextView
android:background="#AA000000"
android:textColor="#FFFFFF"
android:text="Only one museum available"
android:textSize="16sp"
android:padding="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/cityTitleTextView"
android:layout_gravity="bottom"
android:background="#AAFFFFFF"
android:textColor="#000000"
android:textSize="24sp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="48dp" />
</FrameLayout>
<!-- Many museums card -->
<?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="240dp">
<ImageView
android:id="#+id/cityBackgroundImageView"
android:scaleType="fitXY"
android:layout_width="500dp"
android:layout_height="match_parent" />
<TextView
android:id="#+id/cityNumberOrMuseumsTextView"
android:layout_gravity="top|end"
android:background="#AA000000"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:padding="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/cityTitleTextView"
android:layout_gravity="bottom"
android:background="#AA000000"
android:textColor="#FFFFFF"
android:textSize="24sp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="48dp" />
</FrameLayout>
Then we need to create an adapter for our RecyclerView
CityAdapter.java
public class CityAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
final static int ITEM_TYPE_MANY_MUSEUMS = 0;
final static int ITEM_TYPE_ONE_MUSEUM = 1;
private List<City> items;
public CityAdapter(List<City> items) {
this.items = items;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
switch (viewType) {
case ITEM_TYPE_MANY_MUSEUMS:
return new ViewHolder(new CityWithManyMuseumsCardView(viewGroup.getContext()));
case ITEM_TYPE_ONE_MUSEUM:
return new ViewHolder(new CityWithOneMuseumCardView(viewGroup.getContext()));
default:
throw new IllegalArgumentException(String.format("Unexpected viewType: %d", viewType));
}
}
#Override
public int getItemViewType(int position) {
if (items == null || items.size() < position) {
throw new IllegalArgumentException("Wrong position!");
}
if (items.get(position).museums.size() > 1) {
return ITEM_TYPE_MANY_MUSEUMS;
} else if (items.get(position).museums.size() == 1){
return ITEM_TYPE_ONE_MUSEUM;
}
throw new IllegalArgumentException("Wrong number of museums!");
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((IItemDisplayer<City>) holder.itemView).displayItem(items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
}
}
I've uploaded this project to my dropbox - feel free to check it out! Hope, it helps.
Can you try this:
import android.content.Context;
import android.graphics.Rect;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import java.lang.reflect.Field;
/**
* {#link android.support.v7.widget.LinearLayoutManager} which wraps its content. Note that this class will always
* wrap the content regardless of {#link android.support.v7.widget.RecyclerView} layout parameters.
* <p/>
* Now it's impossible to run add/remove animations with child views which have arbitrary dimensions (height for
* VERTICAL orientation and width for HORIZONTAL). However if child views have fixed dimensions
* {#link #setChildSize(int)} method might be used to let the layout manager know how big they are going to be.
* If animations are not used at all then a normal measuring procedure will run and child views will be measured during
* the measure pass.
*/
public class WrapContentLinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {
private static boolean canMakeInsetsDirty = true;
private static Field insetsDirtyField = null;
private static final int CHILD_WIDTH = 0;
private static final int CHILD_HEIGHT = 1;
private static final int DEFAULT_CHILD_SIZE = 100;
private final int[] childDimensions = new int[2];
private final RecyclerView view;
private int childSize = DEFAULT_CHILD_SIZE;
private boolean hasChildSize;
private int overScrollMode = ViewCompat.OVER_SCROLL_ALWAYS;
private final Rect tmpRect = new Rect();
#SuppressWarnings("UnusedDeclaration")
public WrapContentLinearLayoutManager(Context context) {
super(context);
this.view = null;
}
#SuppressWarnings("UnusedDeclaration")
public WrapContentLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
this.view = null;
}
#SuppressWarnings("UnusedDeclaration")
public WrapContentLinearLayoutManager(RecyclerView view) {
super(view.getContext());
this.view = view;
this.overScrollMode = ViewCompat.getOverScrollMode(view);
}
#SuppressWarnings("UnusedDeclaration")
public WrapContentLinearLayoutManager(RecyclerView view, int orientation, boolean reverseLayout) {
super(view.getContext(), orientation, reverseLayout);
this.view = view;
this.overScrollMode = ViewCompat.getOverScrollMode(view);
}
public void setOverScrollMode(int overScrollMode) {
if (overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS || overScrollMode > ViewCompat.OVER_SCROLL_NEVER)
throw new IllegalArgumentException("Unknown overscroll mode: " + overScrollMode);
if (this.view == null) throw new IllegalStateException("view == null");
this.overScrollMode = overScrollMode;
ViewCompat.setOverScrollMode(view, overScrollMode);
}
public static int makeUnspecifiedSpec() {
return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
}
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
final boolean hasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED;
final boolean hasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED;
final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;
final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;
final int unspecified = makeUnspecifiedSpec();
if (exactWidth && exactHeight) {
// in case of exact calculations for both dimensions let's use default "onMeasure" implementation
super.onMeasure(recycler, state, widthSpec, heightSpec);
return;
}
final boolean vertical = getOrientation() == VERTICAL;
initChildDimensions(widthSize, heightSize, vertical);
int width = 0;
int height = 0;
// it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This
// happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the
// recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never
// called whiles scrolling)
recycler.clear();
final int stateItemCount = state.getItemCount();
final int adapterItemCount = getItemCount();
// adapter always contains actual data while state might contain old data (f.e. data before the animation is
// done). As we want to measure the view with actual data we must use data from the adapter and not from the
// state
for (int i = 0; i < adapterItemCount; i++) {
if (vertical) {
if (!hasChildSize) {
if (i < stateItemCount) {
// we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
// we will use previously calculated dimensions
measureChild(recycler, i, widthSize, unspecified, childDimensions);
} else {
logMeasureWarning(i);
}
}
height += childDimensions[CHILD_HEIGHT];
if (i == 0) {
width = childDimensions[CHILD_WIDTH];
}
if (hasHeightSize && height >= heightSize) {
break;
}
} else {
if (!hasChildSize) {
if (i < stateItemCount) {
// we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
// we will use previously calculated dimensions
measureChild(recycler, i, unspecified, heightSize, childDimensions);
} else {
logMeasureWarning(i);
}
}
width += childDimensions[CHILD_WIDTH];
if (i == 0) {
height = childDimensions[CHILD_HEIGHT];
}
if (hasWidthSize && width >= widthSize) {
break;
}
}
}
if (exactWidth) {
width = widthSize;
} else {
width += getPaddingLeft() + getPaddingRight();
if (hasWidthSize) {
width = Math.min(width, widthSize);
}
}
if (exactHeight) {
height = heightSize;
} else {
height += getPaddingTop() + getPaddingBottom();
if (hasHeightSize) {
height = Math.min(height, heightSize);
}
}
setMeasuredDimension(width, height);
if (view != null && overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS) {
final boolean fit = (vertical && (!hasHeightSize || height < heightSize))
|| (!vertical && (!hasWidthSize || width < widthSize));
ViewCompat.setOverScrollMode(view, fit ? ViewCompat.OVER_SCROLL_NEVER : ViewCompat.OVER_SCROLL_ALWAYS);
}
}
private void logMeasureWarning(int child) {
if (BuildConfig.DEBUG) {
Log.w("LinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +
"To remove this message either use #setChildSize() method or don't run RecyclerView animations");
}
}
private void initChildDimensions(int width, int height, boolean vertical) {
if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {
// already initialized, skipping
return;
}
if (vertical) {
childDimensions[CHILD_WIDTH] = width;
childDimensions[CHILD_HEIGHT] = childSize;
} else {
childDimensions[CHILD_WIDTH] = childSize;
childDimensions[CHILD_HEIGHT] = height;
}
}
#Override
public void setOrientation(int orientation) {
// might be called before the constructor of this class is called
//noinspection ConstantConditions
if (childDimensions != null) {
if (getOrientation() != orientation) {
childDimensions[CHILD_WIDTH] = 0;
childDimensions[CHILD_HEIGHT] = 0;
}
}
super.setOrientation(orientation);
}
public void clearChildSize() {
hasChildSize = false;
setChildSize(DEFAULT_CHILD_SIZE);
}
public void setChildSize(int childSize) {
hasChildSize = true;
if (this.childSize != childSize) {
this.childSize = childSize;
requestLayout();
}
}
private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize, int[] dimensions) {
final View child;
try {
child = recycler.getViewForPosition(position);
} catch (IndexOutOfBoundsException e) {
if (BuildConfig.DEBUG) {
Log.w("LinearLayoutManager", "LinearLayoutManager doesn't work well with animations. Consider switching them off", e);
}
return;
}
final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();
final int hPadding = getPaddingLeft() + getPaddingRight();
final int vPadding = getPaddingTop() + getPaddingBottom();
final int hMargin = p.leftMargin + p.rightMargin;
final int vMargin = p.topMargin + p.bottomMargin;
// we must make insets dirty in order calculateItemDecorationsForChild to work
makeInsetsDirty(p);
// this method should be called before any getXxxDecorationXxx() methods
calculateItemDecorationsForChild(child, tmpRect);
final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);
final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);
final int childWidthSpec = getChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());
final int childHeightSpec = getChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.height, canScrollVertically());
child.measure(childWidthSpec, childHeightSpec);
dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;
dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;
// as view is recycled let's not keep old measured values
makeInsetsDirty(p);
recycler.recycleView(child);
}
private static void makeInsetsDirty(RecyclerView.LayoutParams p) {
if (!canMakeInsetsDirty) {
return;
}
try {
if (insetsDirtyField == null) {
insetsDirtyField = RecyclerView.LayoutParams.class.getDeclaredField("mInsetsDirty");
insetsDirtyField.setAccessible(true);
}
insetsDirtyField.set(p, true);
} catch (NoSuchFieldException e) {
onMakeInsertDirtyFailed();
} catch (IllegalAccessException e) {
onMakeInsertDirtyFailed();
}
}
private static void onMakeInsertDirtyFailed() {
canMakeInsetsDirty = false;
if (BuildConfig.DEBUG) {
Log.w("LinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
}
}
}
Put this WrapContentLinearLayoutManager in your recyclerview
mRecyclerView.setLayoutManager(new WrapContentLinearLayoutManager(activity));
And this in the xml (with the relative in parent)
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:id="#+id/recyclerViewMuseum"
android:scrollbars="vertical"
android:scrollIndicators="none"/>
This should work :)
1.Try set LayoutParams CENTER_IN_PARENT when recyclerview has 1 child
2.Or Use a FrameLayout as parent of recyclerView and set layout_gravity
3.Or just compute the space above cardview and set marginTop to recyclerView or setTranslationY to recyclerView
4.Or add a itemDecaration with space'height at Recylerview.
RelativeLayout.LayoutParams rLp = (RelativeLayout.LayoutParams) recyclerView.getLayoutParams();
if(data != null || data.size()>1){
rLp.removeRule(RelativeLayout.CENTER_IN_PARENT);
}else{
rLp.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
}
recyclerView.setLayoutParams(rLp);
When recyclerView has only one child use this LayoutManager:
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
public class FullyLinearLayoutManager extends LinearLayoutManager {
private static final String TAG = FullyLinearLayoutManager.class.getSimpleName();
private MeasureEndListener mMeasureEndListener;
public FullyLinearLayoutManager(Context context) {
super(context);
}
private float divHeight =0;
public FullyLinearLayoutManager(Context context,float height) {
super(context);
divHeight = height;
}
public FullyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
private int[] mMeasuredDimension = new int[2];
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
Log.i(TAG, "onMeasure called. \nwidthMode " + widthMode
+ " \nheightMode " + heightSpec
+ " \nwidthSize " + widthSize
+ " \nheightSize " + heightSize
+ " \ngetItemCount() " + getItemCount());
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
// LogUtils.e(mMeasuredDimension[1]);
height = height + mMeasuredDimension[1];
if(i!= getItemCount()-1){
height += divHeight;
// LogUtils.e(divHeight + "xxx add"+DensityUtils.dp2px(divHeight));
}else{
height += 2*divHeight;
// LogUtils.e(divHeight+ "xxx no add"+ DensityUtils.dp2px(divHeight));
}
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
if(mMeasureEndListener!=null){
mMeasureEndListener.onMeasureEnd(width,height);
// new Thread().interrupt();
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
try {
View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
public interface MeasureEndListener{
void onMeasureEnd(int width,int height);
}
public void setMeasureEndListener(MeasureEndListener mMeasureEndListener){
this.mMeasureEndListener = mMeasureEndListener;
}
}
Try set height of RecyclerView when have only one item or more.
Refer this topic for to do that. When have only one item, set RecyclerView to WRAP_CONTENT. And when have more one item, set RecyclerView to MATCH_CONTENT. Good luck!
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/margin_twenty"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginBottom="#dimen/margin_twenty">
</android.support.v7.widget.RecyclerView>
I create a mobile app for android that show recycler view and slideshow on that page. I throw scroll view before the recyclerview but it need relativelayout because I have more than one component inside the scrollview.
This is my source code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
android:layout_height="fill_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
android:background="#eeeeee"
app:layout_behavior="#string/appbar_scrolling_view_behavior" tools:showIn="#layout/app_bar_main"
tools:context=".MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="fill_parent">
<com.daimajia.slider.library.SliderLayout
android:id="#+id/slider"
android:layout_width="match_parent"
custom:pager_animation="Accordion"
custom:auto_cycle="true"
custom:indicator_visibility="visible"
custom:pager_animation_span="1100"
android:layout_height="200dp"/>
<com.daimajia.slider.library.Indicators.PagerIndicator
android:id="#+id/custom_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
custom:selected_color="#0095BF"
custom:unselected_color="#55333333"
custom:selected_drawable="#drawable/banner1"
custom:shape="oval"
custom:selected_padding_left="5dp"
custom:selected_padding_right="5dp"
custom:unselected_padding_left="5dp"
custom:unselected_padding_right="5dp"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
custom:selected_width="6dp"
custom:selected_height="6dp"
custom:unselected_width="6dp"
custom:unselected_height="6dp"
android:layout_marginBottom="20dp"
/>
<com.daimajia.slider.library.Indicators.PagerIndicator
android:id="#+id/custom_indicator2"
style="#style/AndroidImageSlider_Corner_Oval_Orange"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Produk Terbaru"
android:id="#+id/judul"
android:layout_marginBottom="15dp"
android:layout_marginTop="15dp"
android:layout_below="#id/slider"/>
<view
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/judul"
class="android.support.v7.widget.RecyclerView"
android:id="#+id/recycler_view" />
<ProgressBar
android:id="#+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>
</ScrollView>
</RelativeLayout>
But, RecyclerView not expanded after I add RelativeLayout.
Just use this Layoutmanager:
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import com.apkfuns.logutils.LogUtils;
public class MainActivity{
onCreate(){
recyclerview.setLayoutManager(new FullyLinearLayoutManager(MainActivity.this))
}
public class FullyLinearLayoutManager extends LinearLayoutManager {
private static final String TAG = FullyLinearLayoutManager.class.getSimpleName();
private MeasureEndListener mMeasureEndListener;
public FullyLinearLayoutManager(Context context) {
super(context);
}
private float divHeight =0;
public FullyLinearLayoutManager(Context context,float height) {
super(context);
divHeight = height;
}
public FullyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
private int[] mMeasuredDimension = new int[2];
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
Log.i(TAG, "onMeasure called. \nwidthMode " + widthMode
+ " \nheightMode " + heightSpec
+ " \nwidthSize " + widthSize
+ " \nheightSize " + heightSize
+ " \ngetItemCount() " + getItemCount());
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
// LogUtils.e(mMeasuredDimension[1]);
height = height + mMeasuredDimension[1];
if(i!= getItemCount()-1){
height += divHeight;
// LogUtils.e(divHeight + "xxx add"+DensityUtils.dp2px(divHeight));
}else{
height += 2*divHeight;
// LogUtils.e(divHeight+ "xxx no add"+ DensityUtils.dp2px(divHeight));
}
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
if(mMeasureEndListener!=null){
mMeasureEndListener.onMeasureEnd(width,height);
// new Thread().interrupt();
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
try {
View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
public interface MeasureEndListener{
void onMeasureEnd(int width,int height);
}
public void setMeasureEndListener(MeasureEndListener mMeasureEndListener){
this.mMeasureEndListener = mMeasureEndListener;
}
}
}
I am adding items in listview horizontally but if i have single item i want it to be in center horizontally.
I tried many solution from stack overflow but still help less.
I tried using relative layout(centerInParent, centerHorizontally)
gravity, layout_gravity property etc.
Please help.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:weightSum="10"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/noUser_layout"
android:orientation="vertical"
android:layout_weight="10"
android:visibility="gone"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/noMessageLogo"
android:src="#mipmap/eapp_icon"/>
<TextView android:text="#string/loading_Message" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_marginLeft="25dp"
android:layout_marginRight="25dp"
android:id="#+id/noMessage"
android:gravity="center"
android:fontFamily="sans-serif"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#android:color/black" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/parentLayout"
android:orientation="vertical"
android:layout_weight="3.8"
android:gravity="center|center_horizontal">
<TextView android:text="Parent" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:id="#+id/parentText"
android:gravity="center"
android:fontFamily="sans-serif"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#android:color/holo_blue_dark" />
<android.support.v7.widget.RecyclerView
android:id="#+id/parentList"
android:layout_width="wrap_content"
android:layout_centerHorizontal="true"
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content" android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="2.5"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:id="#+id/contactLayout"
android:layout_centerHorizontal="true"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:id="#+id/userImage"
android:layout_gravity="center"
android:src="#drawable/user_icon" />
<TextView android:text="Contact Name" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:id="#+id/name"
android:fontFamily="sans-serif"
android:textSize="15sp"
android:textStyle="bold"
android:layout_gravity="left"
android:textColor="#android:color/black"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/childrenLayout"
android:layout_centerHorizontal="true"
android:layout_weight="3.8"
android:orientation="vertical"
android:gravity="center|center_horizontal">
<TextView android:text="Child" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/childText"
android:gravity="center"
android:layout_margin="10dp"
android:fontFamily="sans-serif"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#android:color/holo_blue_dark" />
<android.support.v7.widget.RecyclerView
android:id="#+id/childList"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|center_horizontal" />
</LinearLayout>
</LinearLayout>
Use this layout manager on your RecyclerView
public class WrapContentLinearLayoutManager extends LinearLayoutManager {
public WrapContentLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
private int[] mMeasuredDimension = new int[2];
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
height = height + mMeasuredDimension[1];
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
}
}
I have used Window manager to get total width of screen and used to set to recycle view.
The following Layout Manager is for horizontal recycle view i.e. LinearLayoutManager
public class WrapHeightLinearLayoutManager extends LinearLayoutManager {
Context mContext;
public WrapHeightLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
mContext = context;
}
private int[] mMeasuredDimension = new int[2];
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
//width = width + mMeasuredDimension[0];
if (i == 0) {
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int widthWin = size.x;
int heightWin = size.y;
width = widthWin;
height = mMeasuredDimension[1];
}
} else {
height = height + mMeasuredDimension[1];
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
Logger.getInstanceLogger().printVerbose("WrapHeightLinearLayoutManager","width ==>"+width+" height==>"+height);
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
}
}
This simple example solved my problem :)
A summarised code is here:
public class SpinnerWheelActivity extends Activity {
private static final int NUM_ITEMS = 3;
private static final String BUNDLE_LIST_PIXELS = "allPixels";
private float itemWidth;
private float padding;
private float firstItemWidth;
private float allPixels;
private int mLastPosition;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_spinner_wheel);
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
itemWidth = getResources().getDimension(R.dimen.item_width);
padding = (size.x - itemWidth) / 2;
firstItemWidth = getResources().getDimension(R.dimen.padding_item_width);
allPixels = 0;
final RecyclerView items = (RecyclerView) findViewById(R.id.item_list);
LinearLayoutManager shopItemslayoutManager = new LinearLayoutManager(getApplicationContext());
shopItemslayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
items.setLayoutManager(shopItemslayoutManager);
// to center item
items.setOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
synchronized (this) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
calculatePositionAndScroll(recyclerView);
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
allPixels += dx;
}
});
ExtraItemsAdapter adapter = new ExtraItemsAdapter(NUM_ITEMS);
items.setAdapter(adapter);
if (NUM_ITEMS > 0) // init position
scrollListToPosition(items, 0);
}
#Override
protected void onResume() {
super.onResume();
//final RecyclerView items = (RecyclerView) findViewById(R.id.item_list);
//ViewTreeObserver vto = items.getViewTreeObserver();
//vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
// #Override
// public void onGlobalLayout() {
// items.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// calculatePositionAndScroll(items);
// }
//});
}
private void calculatePositionAndScroll(RecyclerView recyclerView) {
int expectedPosition = Math.round((allPixels + padding - firstItemWidth) / itemWidth);
// Special cases for the padding items
if (expectedPosition == -1) {
expectedPosition = 0;
} else if (expectedPosition >= recyclerView.getAdapter().getItemCount() - 2) {
expectedPosition--;
}
scrollListToPosition(recyclerView, expectedPosition);
}
private void scrollListToPosition(RecyclerView recyclerView, int expectedPosition) {
float targetScrollPos = expectedPosition * itemWidth + firstItemWidth - padding;
float missingPx = targetScrollPos - allPixels;
if (missingPx != 0) {
recyclerView.smoothScrollBy((int) missingPx, 0);
}
}
#Override
protected void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
allPixels = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS);
}
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putFloat(BUNDLE_LIST_PIXELS, allPixels);
}
/*---------- Adapters ----------*/
public class ExtraItemsAdapter extends RecyclerView.Adapter<ViewHolder> {
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private final int mNumItems;
public ExtraItemsAdapter(int numItems) {
mNumItems = numItems;
}
#Override
public int getItemCount() {
return mNumItems + 2; // We have to add 2 paddings
}
#Override
public int getItemViewType(int position) {
if (position == 0 || position == getItemCount() - 1) {
return VIEW_TYPE_PADDING;
}
return VIEW_TYPE_ITEM;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
if (viewType == VIEW_TYPE_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
return new ViewHolder(v);
} else {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_padding, parent, false);
return new ViewHolder(v);
}
}
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
// We bind the item to the view
holder.text.setText(String.valueOf(position));
}
}
}
/*---------- ViewHolders ----------*/
public class ViewHolder extends RecyclerView.ViewHolder {
TextView text;
public ViewHolder(View itemView) {
super(itemView);
text = (TextView) itemView.findViewById(R.id.item_text);
}
}
}