I'm stuck with an issue about changing Recycler height based on its total items.
What I have tried is to use Layout Param like this:
ViewGroup.LayoutParams params = myRecyclerView.getLayoutParams();
params.height = itemHeight * numberOfItem;
myRecyclerView.requestLayout();
or
ViewGroup.LayoutParams params = new RecyclerView.LayoutParams(..WRAP_CONTENT, ...WRAP_CONTENT);;
params.height = itemHeight * numberOfItem;
myRecyclerView..setLayoutParams(params);
But it didn't work.
How can I do it ? Please help me !
You should use LayoutParams of parent's view in setLayoutParams(params).
For example:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<android.support.v7.widget.RecyclerView
android:id="#+id/images"
android:layout_width="wrap_content"
android:layout_height="360dp"
>
</android.support.v7.widget.RecyclerView>
</Relativelayout>
Change LayoutParams in the code.
RelativeLayout.LayoutParams lp =
new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 500);
recyclerView.setLayoutParams(lp);
I tried this. It worked. May be help.
#Override
public void onBindViewHolder(FeedListRowHolder feedListRowHolder, int i) {
//this change height of rcv
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.height =80; //height recycleviewer
feedListRowHolder.itemView.setLayoutParams(params);
FeedItem feedItem = feedItemList.get(i);
Picasso.with(mContext).load(feedItem.getThumbnail())
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.into(feedListRowHolder.thumbnail);
feedListRowHolder.title.setText(Html.fromHtml(feedItem.getTitle()));
feedListRowHolder.itemView.setActivated(selectedItems.get(i, false));
feedListRowHolder.setClickListener(new FeedListRowHolder.ClickListener() {
public void onClick(View v, int pos, boolean isLongClick) {
if (isLongClick) {
// View v at position pos is long-clicked.
String poslx = pos + "";
Toast.makeText(mContext, "longclick " + poslx, Toast.LENGTH_SHORT).show();
} else {
// View v at position pos is clicked.
String possx = pos + "";
Toast.makeText(mContext, "shortclick " + possx, Toast.LENGTH_SHORT).show();
toggleSelection(pos);
}
}
});
}
If you just want your recycler view to size automatically as per number of items then, why don't you put RecyclerView height as wrap_content.
If you have multiple ScrollView in layout then try wrapping RecyclerView in NestScrollView and set it's height as wrap_content too.
code :
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
Although the question was asked quite some time ago, I figured some might find this answer helpful.
I have a RecyclerView with adapter. The height is set in onBindViewHolder method of the adapter:
Layout:
<android.support.v7.widget.RecyclerView
android:id="#+id/container"
...
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
public abstract static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
public abstract void setFixedHeight();
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
return new ViewHolder(view) {
#Override
public void setFixedHeight() {
// magic happening here
ViewGroup.LayoutParams parentParams = parent.getLayoutParams();
parentParams.height =
((RecyclerView) parent).computeVerticalScrollRange()
+ parent.getPaddingTop()
+ parent.getPaddingBottom();
parent.setLayoutParams(parentParams);
}
};
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.setFixedHeight();
}
// other methods here
}
Setting adapter:
recyclerView.setAdapter(new MyAdapter(...));
Note: Use((RecyclerView) parent).computeHorizontalScrollRange() with horizontal scroll
This code works I am sure about it
ViewGroup.LayoutParams params=recyclerview.getLayoutParams();
params.height=100;
recyclerview.setLayoutParams(params);
The other thing you could do is make a linear layout as the parent of the recycler view and then increase the height dynamically for the parent view
Consider the following XML below
<LinearLayout
android:id="#+id/yourLayoutId">
<RecyclerView android:width="match_parent" android:height="wrap_content">
</RecyclerView>
</LinearLayout
Consider the following code
LinearLayout layout = (LinearLayout)findViewById(R.id.yourLayoutId);
LinearLayout.LayoutParams lp = (LayoutParams) layout.getLayoutParams();
lp.height = 100;
Related
I am having 15 to 30 items in my recyclerview. At the End of the recyclerview I want to show the Image/Layout at bottom. This image will slowly come to top while scroll the recylerview to top. When the list end the image/layout will fully shown. If we scroll down the recyclerview the image/layout should go down. If I stop the scroll at middle the image/layout will show partially. For example the Image/Layout height will be 100 dp. it will be placed in the bottom. It will not visible at first time. When we scroll the Recyclerview that view will be slowly appear. Please give me any idea to achieve this. Sorry for my bad English.
<?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.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical" />
<RelativeLayout
android:id="#+id/bottomView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Will show while Scroll"
android:textSize="30sp"
/>
</RelativeLayout>
</RelativeLayout>
Scrolling Recyclerview
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
footerHeight = +10;
bottomView.setTranslationY(footerHeight);
Log.i("Test","...Scrolling up");
} else {
footerHeight = -10;
bottomView.setTranslationY(footerHeight);
Log.i("Test","...Scrolling down");
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE:
Log.i("Test","...The RecyclerView is not scrolling");
break;
case RecyclerView.SCROLL_STATE_DRAGGING:
Log.i("Test","...Scrolling now");
break;
case RecyclerView.SCROLL_STATE_SETTLING:
Log.i("Test","...Scroll Settling");
break;
}
}
});
Here I just increase/decrease the bottomX view while scrolling. But still I am missing something.
OP:
In this image bottom view is showing always. But initially it want view should be hidden state. While scroll up Bottom view slowly come up. If I scroll down Bottom view should slowly goes down.
Start a new project and try this:
MainActivity.java:
public class MainActivity extends AppCompatActivity {
private static final int DATA_LIST_SIZE = 50;
RecyclerView recyclerView;
TextView footer;
ArrayList<SampleData> dataArrayList;
LinearLayoutManager linearLayoutManager;
int totalHeight = -1;
int invisibleHeight = -1;
int scrolledHeight = -1;
int childHeight = -1;
int footerHeight = -1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recycler_view);
footer = findViewById(R.id.text_view_footer);
dataArrayList = genSampleDataList();
CustomRecyclerViewAdapter adapter = new CustomRecyclerViewAdapter(dataArrayList);
linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
footer.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
footerHeight = footer.getMeasuredHeight();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
View firstVisibleView = recyclerView.getChildAt(0);
if (invisibleHeight == -1) {
childHeight = linearLayoutManager.getDecoratedMeasuredHeight(firstVisibleView);
totalHeight = childHeight * DATA_LIST_SIZE;
invisibleHeight = totalHeight - recyclerView.getHeight() + footerHeight;
}
scrolledHeight = linearLayoutManager.findFirstVisibleItemPosition() * childHeight +
recyclerView.getTop() - firstVisibleView.getTop();
int newRecyclerViewHeight = totalHeight - invisibleHeight + footerHeight -
scrolledHeight * footerHeight / invisibleHeight;
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, newRecyclerViewHeight);
recyclerView.setLayoutParams(params);
footer.setBackgroundColor(Color.rgb(255 * (invisibleHeight - scrolledHeight) / invisibleHeight,
255 * scrolledHeight / invisibleHeight, 0));
}
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
}
});
}
private ArrayList<SampleData> genSampleDataList() {
ArrayList<SampleData> tmpList = new ArrayList<>();
for (int i = 0; i < DATA_LIST_SIZE; i++) {
tmpList.add(new SampleData("Item " + (i + 1), "Description " + (i + 1)));
}
return tmpList;
}
}
SampleData.java:
public class SampleData {
String name;
String description;
public SampleData(String name, String description) {
this.name = name;
this.description = description;
}
}
CustomRecyclerViewAdapter.java:
public class CustomRecyclerViewAdapter extends RecyclerView.Adapter<CustomRecyclerViewAdapter.ViewHolder> {
ArrayList<SampleData> dataList;
public CustomRecyclerViewAdapter(ArrayList<SampleData> dataList) {
this.dataList = dataList;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, null);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
SampleData sampleData = dataList.get(position);
holder.textViewName.setText(sampleData.name);
holder.textViewDescription.setText(sampleData.description);
}
#Override
public int getItemCount() {
return dataList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView textViewName;
TextView textViewDescription;
public ViewHolder(#NonNull View itemView) {
super(itemView);
textViewName = itemView.findViewById(R.id.text_view_name);
textViewDescription = itemView.findViewById(R.id.text_view_description);
}
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:scrollbars="vertical" />
<TextView
android:id="#+id/text_view_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/recycler_view"
android:gravity="center"
android:text="Will show while Scroll"
android:textSize="30sp" />
</RelativeLayout>
item_view.xml:
<TextView
android:id="#+id/text_view_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="25sp"
android:textStyle="bold" />
<TextView
android:id="#+id/text_view_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp" />
</LinearLayout>
One solution if I've read your question correctly is in your model class to include link or Uri of ImageView in a String.
Then in your RecyclerView adapter do some boolean checking to see if item added has a link to it and if it has load it with library called Picasso for example. Picasso is simple just one line of code. If you are using image from phone you might just add uri to image.
And when items are added on last item add link to image or set it yourself.
I try to do something like this :
I managed to do my cardViewAdapter but I block to enlarge my cards. I resumed the code of this response (Here the name of the class is : CardsAnimationHelper) to do the animation but it's superimposed.
Before expand:
After expand:
I solved the problem above but if on my cardView I display 10 elements at the same time for a list of 50. If I expand the first, the numbers 11,21,31,41 will also expand. Do you have a trick for this not to happen?
I have reflected, it makes no sense to me. Just before my OnClick method I display a textview where the text is the position. But when I click id are correct so that would mean that when I click it detects the click on several cards. I think I may have a problem with a view in my OnClickListener
My CardView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
app:cardBackgroundColor="#android:color/white"
app:cardCornerRadius="2dp"
app:cardElevation="2dp">
<!-- Les CardView possèdent des attributs supplémentaires dont
- cardBackgroundColor
- cardElevation pour l'élévation (donc aussi l'ombre)
- cardCornerRadius pour arrondir les angles
-->
<android.support.design.widget.CoordinatorLayout 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">
<!-- Les CardView agissent comme des FrameLayout,
pour avoir une organisation verticale nous devons
donc rajouter un LinearLayout -->
<TextView
android:id="#+id/text_cards"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:padding="20dp"
tools:text="Paris"
android:fontFamily="sans-serif"
android:textColor="#333"
android:textSize="18sp" />
<ImageView
android:id="#+id/item_description_game_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:transitionName="#string/transition_cards_view"
app:srcCompat="#drawable/ic_expand_more_black_24dp"/>
<include layout="#layout/cards_resume_game_expand"/>
</android.support.design.widget.CoordinatorLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
My New Adapter
public class CardsViewAdapter extends RecyclerView.Adapter<CardsViewAdapter.ViewHolder> {
private Game[] mDataset;
private boolean isPopupVisible = false;
int rotationAngle = 0;
// 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 ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView mTextView;
public ImageView imageView;
public LinearLayout test2;
public ViewHolder(View v) {
super(v);
mTextView = (TextView) v.findViewById(R.id.text_cards);
imageView = (ImageView) v.findViewById(R.id.item_description_game_more);
test2 = (LinearLayout) v.findViewById(R.id.popup_layout);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public CardsViewAdapter(Game[] myDataset) {
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public CardsViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cards_resume_game, parent, false);
// set the view's size, margins, paddings and layout parameters
//...
ViewHolder vh = new ViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
holder.mTextView.setText(String.valueOf(mDataset[position].getId_game()));
holder.imageView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
if (isPopupVisible) {
isPopupVisible = false;
ObjectAnimator anim = ObjectAnimator.ofFloat(v, "rotation",rotationAngle, rotationAngle + 180);
anim.setDuration(500);
anim.start();
rotationAngle += 180;
rotationAngle = rotationAngle%360;
// CardsAnimationHelper.changeIconAnim((TextView) v, getString(R.string.icon_chevron_up));
CardsAnimationHelper.collapse(holder.test2);
} else {
isPopupVisible = true;
ObjectAnimator anim = ObjectAnimator.ofFloat(v, "rotation",rotationAngle, rotationAngle + 180);
anim.setDuration(500);
anim.start();
rotationAngle += 180;
rotationAngle = rotationAngle%360;
// CardsAnimationHelper.changeIconAnim((TextView) v, getString(R.string.icon_chevron_down));
CardsAnimationHelper.expand(holder.test2);
}
}
});
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataset.length;
}
}
I did not understand what you meant by displaying 10 elements out of 50. However, you can achieve the expand/collapse simply by showing/hiding the views and providing android:animateLayoutChanges="true" into the child layout of the CardView. Here is an example:
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:padding="16dp"
android:orientation="vertical">
<TextView
android:id="#+id/hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"/>
<TextView
android:id="#+id/hello2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:visibility="gone"/>
<TextView
android:id="#+id/hello3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:visibility="gone"/>
</LinearLayout>
</android.support.v7.widget.CardView>
And corresponding controller:
TextView t1 = (TextView) findViewById(R.id.hello);
final TextView t2 = (TextView) findViewById(R.id.hello2);
final TextView t3 = (TextView) findViewById(R.id.hello3);
t1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (t2.getVisibility() == View.GONE) {
t2.setVisibility(View.VISIBLE);
t3.setVisibility(View.VISIBLE);
} else {
t2.setVisibility(View.GONE);
t3.setVisibility(View.GONE);
}
}
});
Tapping on the first TextView will collapse and expand the CardView along with the animation.
You'll need to create a custom class that extends CardView. Inside that class put the following methods:
public void expand() {
int initialHeight = getHeight();
measure(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
int targetHeight = getMeasuredHeight();
int distanceToExpand = targetHeight - initialHeight;
Animation a = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (interpolatedTime == 1){
// Do this after expanded
}
getLayoutParams().height = (int) (initialHeight + (distanceToExpand * interpolatedTime));
requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration((long) distanceToExpand);
startAnimation(a);
}
public void collapse(int collapsedHeight) {
int initialHeight = getMeasuredHeight();
int distanceToCollapse = (int) (initialHeight - collapsedHeight);
Animation a = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (interpolatedTime == 1){
// Do this after collapsed
}
Log.i(TAG, "Collapse | InterpolatedTime = " + interpolatedTime);
getLayoutParams().height = (int) (initialHeight - (distanceToCollapse * interpolatedTime));
requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration((long) distanceToCollapse);
startAnimation(a);
}
Note that when you collapse it, you'll need to pass along the height you want it to be when collapsed. The height when expanded is set to WRAP_CONTENT.
I've also added if/else statements that will run when the animation has completed.
Good luck!
RecyclerView by default, does come with a nice deletion animation, as long as you setHasStableIds(true) and provide correct implementation on getItemId.
Recently, I had added divider into RecyclerView via https://stackoverflow.com/a/27037230/72437
The outcome looks as following
https://www.youtube.com/watch?v=u-2kPZwF_0w
https://youtu.be/c81OsFAL3zY (To make the dividers more visible when delete animation played, I temporary change the RecyclerView background to red)
The dividers are still visible, when deletion animation being played.
However, if I look at GMail example, when deletion animation being played, divider lines are no longer visible. They are being covered a solid color area.
https://www.youtube.com/watch?v=cLs7paU-BIg
May I know, how can I achieve the same effect as GMail, by not showing divider lines, when deletion animation played?
The solution is fairly easy. To animate a decoration, you can and should use view.getTranslation_() and view.getAlpha(). I wrote a blog post some time ago on this exact issue, you can read it here.
Translation and fading off
The default layout manager will fade views out (alpha) and translate them, when they get added or removed. You have to account for this in your decoration.
The idea is simple:
However you draw your decoration, apply the same alpha and translation to your drawing by using view.getAlpha() and view.getTranslationY().
Following your linked answer, it would have to be adapted like the following:
// translate
int top = child.getBottom() + params.bottomMargin + view.getTranslationY();
int bottom = top + mDivider.getIntrinsicHeight();
// apply alpha
mDivider.setAlpha((int) child.getAlpha() * 255f);
mDivider.setBounds(left + view.getTranslationX(), top,
right + view.getTranslationX(), bottom);
mDivider.draw(c);
A complete sample
I like to draw things myself, since I think drawing a line is less overhead than layouting a drawable, this would look like the following:
public class SeparatorDecoration extends RecyclerView.ItemDecoration {
private final Paint mPaint;
private final int mAlpha;
public SeparatorDecoration(#ColorInt int color, float width) {
mPaint = new Paint();
mPaint.setColor(color);
mPaint.setStrokeWidth(width);
mAlpha = mPaint.getAlpha();
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// we retrieve the position in the list
final int position = params.getViewAdapterPosition();
// add space for the separator to the bottom of every view but the last one
if (position < state.getItemCount()) {
outRect.set(0, 0, 0, (int) mPaint.getStrokeWidth()); // left, top, right, bottom
} else {
outRect.setEmpty(); // 0, 0, 0, 0
}
}
#Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
// a line will draw half its size to top and bottom,
// hence the offset to place it correctly
final int offset = (int) (mPaint.getStrokeWidth() / 2);
// this will iterate over every visible view
for (int i = 0; i < parent.getChildCount(); i++) {
final View view = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// get the position
final int position = params.getViewAdapterPosition();
// and finally draw the separator
if (position < state.getItemCount()) {
// apply alpha to support animations
mPaint.setAlpha((int) (view.getAlpha() * mAlpha));
float positionY = view.getBottom() + offset + view.getTranslationY();
// do the drawing
c.drawLine(view.getLeft() + view.getTranslationX(),
positionY,
view.getRight() + view.getTranslationX(),
positionY,
mPaint);
}
}
}
}
Firstly, sorry for the massive answer size. However, I felt it necessary to include my entire test Activity so that you can see what I have done.
The issue
The issue that you have, is that the DividerItemDecoration has no idea of the state of your row. It does not know whether the item is being deleted.
For this reason, I made a POJO that we can use to contain an integer (that we use as both an itemId and a visual representation and a boolean indicating that this row is being deleted or not.
When you decide to delete entries (in this example adapter.notifyItemRangeRemoved(3, 8);), you must also set the associated Pojo to being deleted (in this example pojo.beingDeleted = true;).
The position of the divider when beingDeleted, is reset to the colour of the parent view. In order to cover up the divider.
I am not very fond of using the dataset itself to manage the state of its parent list. There is perhaps a better way.
The result visualized
The Activity:
public class MainActivity extends AppCompatActivity {
private static final int VERTICAL_ITEM_SPACE = 8;
private List<Pojo> mDataset = new ArrayList<Pojo>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for(int i = 0; i < 30; i++) {
mDataset.add(new Pojo(i));
}
final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new VerticalSpaceItemDecoration(VERTICAL_ITEM_SPACE));
recyclerView.addItemDecoration(new DividerItemDecoration(this));
RecyclerView.ItemAnimator ia = recyclerView.getItemAnimator();
ia.setRemoveDuration(4000);
final Adapter adapter = new Adapter(mDataset);
recyclerView.setAdapter(adapter);
(new Handler(Looper.getMainLooper())).postDelayed(new Runnable() {
#Override
public void run() {
int index = 0;
Iterator<Pojo> it = mDataset.iterator();
while(it.hasNext()) {
Pojo pojo = it.next();
if(index >= 3 && index <= 10) {
pojo.beingDeleted = true;
it.remove();
}
index++;
}
adapter.notifyItemRangeRemoved(3, 8);
}
}, 2000);
}
public class Adapter extends RecyclerView.Adapter<Holder> {
private List<Pojo> mDataset;
public Adapter(#NonNull final List<Pojo> dataset) {
setHasStableIds(true);
mDataset = dataset;
}
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_cell, parent, false);
return new Holder(view);
}
#Override
public void onBindViewHolder(final Holder holder, final int position) {
final Pojo data = mDataset.get(position);
holder.itemView.setTag(data);
holder.textView.setText("Test "+data.dataItem);
}
#Override
public long getItemId(int position) {
return mDataset.get(position).dataItem;
}
#Override
public int getItemCount() {
return mDataset.size();
}
}
public class Holder extends RecyclerView.ViewHolder {
public TextView textView;
public Holder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.text);
}
}
public class Pojo {
public int dataItem;
public boolean beingDeleted = false;
public Pojo(int dataItem) {
this.dataItem = dataItem;
}
}
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private final int[] ATTRS = new int[]{android.R.attr.listDivider};
private Paint mOverwritePaint;
private Drawable mDivider;
/**
* Default divider will be used
*/
public DividerItemDecoration(Context context) {
final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS);
mDivider = styledAttributes.getDrawable(0);
styledAttributes.recycle();
initializePaint();
}
/**
* Custom divider will be used
*/
public DividerItemDecoration(Context context, int resId) {
mDivider = ContextCompat.getDrawable(context, resId);
initializePaint();
}
private void initializePaint() {
mOverwritePaint = new Paint();
mOverwritePaint.setColor(ContextCompat.getColor(MainActivity.this, android.R.color.background_light));
}
#Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
Pojo item = (Pojo) child.getTag();
if(item.beingDeleted) {
c.drawRect(left, top, right, bottom, mOverwritePaint);
} else {
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
}
}
public class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {
private final int mVerticalSpaceHeight;
public VerticalSpaceItemDecoration(int mVerticalSpaceHeight) {
this.mVerticalSpaceHeight = mVerticalSpaceHeight;
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
outRect.bottom = mVerticalSpaceHeight;
}
}
}
The Activity Layout
<?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: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="#android:color/background_light"
tools:context="test.dae.myapplication.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
The RecyclerView "row" Layout
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/text"
android:padding="8dp">
</TextView>
I think the ItemDecorator you use to draw a divider after every row is messing things up when swipe to delete is performed.
Instead of Using ItemDecorator to draw a Divider in a recyclerview, add a view at the end of your RecyclerView child layout design.which will draw a divider line like ItemDecorator.
<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="wrap_content"
>
<!-- child layout Design !-->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#android:color/darker_gray"
android:layout_gravity="bottom"
/>
</Linearlayout>
I am creating a horisontal RecyclerView in my app.
It has to show 2 images on the screen at a time (so width of each image has to be 50% of the screen).
For now it works fine but each item consums all width of the screen.
Here is my code
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_main_ads);
LinearLayoutManager mLinearLayoutManager = new LinearLayoutManager(getActivity());
mLinearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(tmp, R.layout.lv_main_screen);
mRecyclerView.setAdapter(adapter);
Here is layout of an item
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1">
<ImageView
android:id="#+id/iv_main_ad"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:scaleType="fitXY"
android:src="#drawable/baner_gasoline"
/>
</LinearLayout>
As you can see I tried to use Layout_gravity="0.5",
But it doesn't help.
I tried to specify layout_width = ...dp but I can not get exactly half of the screen.
I am thinking of adding another ImageView into item layout, but in this case I will have troubles with the adapter, because I want to implemnt circled (infinity) horizontal listview
here is my adapter:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyHolder> {
private List<Integer> mImages;
private int itemLayout;
public RecyclerViewAdapter(ArrayList<Integer> imageResourceIds, int itemLayout) {
this.mImages = imageResourceIds;
this.itemLayout = itemLayout;
}
#Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false);
return new MyHolder(v);
}
#Override
public void onBindViewHolder(MyHolder holder, int position) {
holder.adIv.setImageResource(mImages.get(position));
}
#Override
public int getItemCount() {
return mImages.size();
}
public static class MyHolder extends RecyclerView.ViewHolder {
protected ImageView adIv;
private MyHolder(View v) {
super(v);
this.adIv = (ImageView) v.findViewById(R.id.iv_main_ad);
}
}
}
For You need to calculate the width of the screen and set the width dynamically below is the my code
Add below code in your ViewHolder initilisation
llImg = (LinearLayout) itemView.findViewById(R.id.llImg);
llImg.getLayoutParams().width = (int) (Utils.getScreenWidth(itemView.getContext()) / 2);
llImg.getLayoutParams().height = (int) (Utils.getScreenWidth(itemView.getContext()) / 2);
imgView = (ImageView) itemView.findViewById(R.id.imgView);
The Layout file is here
<LinearLayout
android:id="#+id/llImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center">
<ImageView
android:id="#+id/imgView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
Make one Utils.java
public static int getScreenWidth(Context context) {
if (screenWidth == 0) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
screenWidth = size.x;
}
return screenWidth;
}
Hope this will help you !
I'm using the twoway view for the first and i'm struggling to specify the layout attributes for the views. I have tow kinds of views, header and the episode.
This is the xml of both:
Show:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_margin="#dimen/margin_16dp"
android:layout_width="248dp"
android:layout_height="160dp"
android:background="#color/black">
//More views...
</RelativeLayout>
Header:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="54dp"
android:background="#color/red_800">
//More views
I set the height to be 54dp in the header and 160dp for the show but what i'm getting is this, it's always the same (wrong) height, the red bar is the header.
http://postimg.org/image/xx1dk45n9/
Code:
public static class ViewHolder extends RecyclerView.ViewHolder{
public TextView title,date,rate,episode;
public ImageView image;
public ViewHolder(View view) {
super(view);
image = (ImageView)view.findViewById(R.id.image);
title = (TextView)view.findViewById(R.id.title);
date = (TextView)view.findViewById(R.id.date);
rate = (TextView)view.findViewById(R.id.rate);
episode = (TextView)view.findViewById(R.id.episode);
}
}
public static class ViewHolderHeader extends RecyclerView.ViewHolder{
public TextView date,children;
public ViewHolderHeader(View view) {
super(view);
date = (TextView)view.findViewById(R.id.date);
children = (TextView)view.findViewById(R.id.children);
}
}
public CalendarAdapter(Context context) {
mContext = context;
readDateFormat(context);
simpleDateFormat = new SimpleDateFormat(datePattern, Locale.US);
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view;
if (viewType == CalendarGridItem.TYPE.SHOW.ordinal()){
view = LayoutInflater.from(mContext).inflate(R.layout.calendar_grid_tile, parent, false);
return new ViewHolder(view);
}
else {
view = LayoutInflater.from(mContext).inflate(R.layout.calendar_grid_header, parent, false);
return new ViewHolderHeader(view);
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final View view = holder.itemView;
final CalendarGridItem item = mShows.get(position);
final SpannableGridLayoutManager.LayoutParams lp =
(SpannableGridLayoutManager.LayoutParams) view.getLayoutParams();
if (getItemViewType(position) == CalendarGridItem.TYPE.HEADER.ordinal()){ //Header
final ViewHolderHeader holder1 = (ViewHolderHeader)holder;
CalendarHeader header = (CalendarHeader)item;
Log.d(TAG,"Entrou header: "+header.getDate());
//Mete a colSpan a 2
if (lp.colSpan != 2)
lp.colSpan = 2;
lp.height = 100; //Even forcing the height doesn't work
view.setLayoutParams(lp);
}else{ //Show
final ViewHolder holder1 = (ViewHolder)holder;
UpcomingShow show = (UpcomingShow)item;
if (lp.colSpan != 1)
lp.colSpan = 1;
view.setLayoutParams(lp);
}
}
Fragment layout:
<org.lucasr.twowayview.widget.TwoWayView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/spanGrid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/background"
style="#style/TwoWayView"
app:twowayview_layoutManager="SpannableGridLayoutManager"
app:twowayview_numColumns="2"/>
What i'm doing wrong ? I'm not used to the recycler view way of doing things, i must be missing something. Teste with Android 4.1.1 and 4.4
Currently have quite the same problem (mine is that elements are all "squares")
It seems like it not possible to give specific height/width for elements in the item layout.
When you look in the source code, in the SpannableGridLayoutManager.java file, you have the method : LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp).
In there, those are the incrimated lines :
final LayoutParams spannableLp = new LayoutParams((MarginLayoutParams) lp);
spannableLp.width = LayoutParams.MATCH_PARENT;
spannableLp.height = LayoutParams.MATCH_PARENT;
But if you do remove that, you "break" all the logic for the spannable grid view...
I also thought that TwoWayView can display the items as squares. So I was digging in the source and found , like Wicha said, that MATCH_PARENT in two places was causing the undesired behaviour.
So I made the following adjustments in the SpannableGridLayoutManager file (commented some lines in checkLayoutParams method) :
#Override
public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
// if (lp.width != LayoutParams.MATCH_PARENT ||
// lp.height != LayoutParams.MATCH_PARENT) {
// return false;
// }
if (lp instanceof LayoutParams) {
(.....)
}
and here (use width and height from layout params in generateLayoutParams method) :
#Override
public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
final LayoutParams spannableLp = new LayoutParams((MarginLayoutParams) lp);
spannableLp.width = lp.width; //LayoutParams.MATCH_PARENT;
spannableLp.height = lp.height; //LayoutParams.MATCH_PARENT;
if (lp instanceof LayoutParams) {
(....)
}
Also, in your Adapter class, goes something like this :
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int i) {
viewHolder.image.setScaleType(ImageView.ScaleType.FIT_XY);
viewHolder.image.setPadding(5,5,5,5);
SpannableGridLayoutManager.LayoutParams layoutParams = ((SpannableGridLayoutManager.LayoutParams) viewHolder.itemView.getLayoutParams());
layoutParams.width = cell_width * item.size; //multiply size
layoutParams.height = cell_height * item.size;//multiply size
layoutParams.colSpan = item.size;
layoutParams.rowSpan = item.size;
viewHolder.image.setLayoutParams(layoutParams);
}
Some observations:
cell_width is calculated like screen width/3 in portrait orientation and screen width/4 in landscape.
cell_height is calculated like image_width / image_ratio.
item.size : I decided to have 1:1 or 2:2 or 3:3 aspect ratios for the items, so item_size is the multiplication value.