Weird behavior when scrolling StaggeredGridLayout - android

I have a RecyclerView to show some images loaded by Picasso. As they have different aspect ratios, I'm using a StaggeredGridLayoutManager. Everything looks good, but when scrolling and a new image shows up, images move up or down a little, like they are getting been relocated. They dont stay in the same place.
The thing is, if I use a GridLayout, everything works perfectly, looks like having different item's sizes messes it up.
This is the ActivityMain class:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Especificamos el layout 'products_grid.xml'
setContentView(R.layout.products_grid);
_initData();
_initAuxViews();
_initToolbar();
_initNavigationDrawers();
_initAnimations();
new ConnectToServer().execute();
}
protected void _initRecyclerView()
{
mProductsRecyclerView = (RecyclerView)findViewById(R.id.grid_recycler);
mStaggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mProductAdapter = new ProductsGridAdapter(this, mProductsDisplayedList);
mProductsRecyclerView.setLayoutManager(mStaggeredGridLayoutManager);
mProductsRecyclerView.setAdapter(mProductAdapter);
mProductsRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener()
{
...
}
}
main.xml, I have a custom RecyclerView, but this does not affect, I checked it.
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.DrawerLayout
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/shop_background_bw">
<!-- Contenido principal -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Recylcer Grid -->
<com.wallakoala.wallakoala.Views.GridRecyclerView
android:id="#+id/grid_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:clipToPadding="false"
android:paddingTop="?attr/actionBarSize"
android:layoutAnimation="#anim/grid_layout_animation"/>
<!-- Toolbar -->
<include android:id="#+id/appbar"
layout="#layout/toolbar">
</include>
<!-- Loading View -->
<com.wang.avi.AVLoadingIndicatorView
android:id="#+id/avloadingIndicatorView"
android:layout_width="125dp"
android:layout_height="125dp"
android:layout_gravity="center"
android:visibility="gone"
app:indicator="BallClipRotate"
app:indicator_color="#color/colorAccent"/>
<!-- Texto de no prductos -->
<TextView
android:id="#+id/nodata_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/action_bar_height"
android:layout_gravity="center_horizontal"
android:visibility="gone"
android:paddingTop="#dimen/action_bar_height"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="#string/nodata_message"
android:textSize="20sp"
android:textColor="#color/colorText"/>
</FrameLayout>
<include layout="#layout/left_navigation_drawer"/>
</android.support.v4.widget.DrawerLayout>
</android.support.design.widget.CoordinatorLayout>
item_grid.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/card_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardPreventCornerOverlap="false"
app:cardElevation="4dp"
app:cardCornerRadius="4dp"
app:cardUseCompatPadding="true">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Loading View -->
<com.wang.avi.AVLoadingIndicatorView
android:id="#+id/avloadingitem"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:visibility="gone"
app:indicator="BallClipRotate"
app:indicator_color="#color/colorAccent"/>
<!-- Background-->
<com.makeramen.roundedimageview.RoundedImageView
android:id="#+id/grid_background"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#color/colorText"
android:alpha="0.2"
android:visibility="gone"
app:riv_corner_radius_bottom_left="4dp"
app:riv_corner_radius_bottom_right="4dp"
app:riv_corner_radius_top_left="4dp"
app:riv_corner_radius_top_right="4dp"/>
<!-- Main Image-->
<com.makeramen.roundedimageview.RoundedImageView
android:id="#+id/grid_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
app:riv_corner_radius_bottom_left="4dp"
app:riv_corner_radius_bottom_right="4dp"
app:riv_corner_radius_top_left="4dp"
app:riv_corner_radius_top_right="4dp"/>
<!-- Footer -->
<RelativeLayout
android:id="#+id/footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:alpha="0.75">
<!-- Info extra -->
<include android:id="#+id/extraInfo"
layout="#layout/product_footer_extra"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"/>
<!-- Info principal -->
<include android:id="#+id/mainFooter"
layout="#layout/product_footer"
android:layout_height="#dimen/footer_height"
android:layout_width="match_parent"
android:layout_below="#id/extraInfo"/>
</RelativeLayout>
</FrameLayout>
</android.support.v7.widget.CardView>
EDIT: Adapter code:
public class ProductsGridAdapter extends RecyclerView.Adapter<ProductsGridAdapter.ProductHolder>
{
/* Constants */
private static final String TAG = "CUOKA";
private static final String PACKAGE = "com.wallakoala.wallakoala";
/* Context */
private static Context mContext;
/* Data */
private static List<Product> mProductList;
public static class ProductHolder extends RecyclerView.ViewHolder implements View.OnClickListener
{
private Product mProduct;
private ImageButton mFavImageButton;
private ImageView mProductImageView;
private ImageView mErrorImageView;
private View mLoadingView;
private View mBackgroundView;
private View mProductFooterView, mProductFooterExtraView, mProductFooterMainView;
private TextView mTitleTextView, mSubtitleTextView, mNameTextView, mPriceTextView;
private Animation scaleUpFooterExtra, scaleDownFooterExtra;
public ProductHolder(View itemView)
{
super(itemView);
mErrorImageView = (ImageView)itemView.findViewById(R.id.broken_image);
mTitleTextView = (TextView)itemView.findViewById(R.id.footer_title);
mSubtitleTextView = (TextView)itemView.findViewById(R.id.footer_subtitle);
mProductImageView = (ImageView)itemView.findViewById(R.id.grid_image);
mFavImageButton = (ImageButton)itemView.findViewById(R.id.footer_fav_button);
mNameTextView = (TextView)itemView.findViewById(R.id.name);
mPriceTextView = (TextView)itemView.findViewById(R.id.price);
mBackgroundView = itemView.findViewById(R.id.grid_background);
mLoadingView = itemView.findViewById(R.id.avloadingitem);
mProductFooterView = itemView.findViewById(R.id.footer);
mProductFooterExtraView = itemView.findViewById(R.id.extraInfo);
mProductFooterMainView = itemView.findViewById(R.id.mainFooter);
mProductFooterView.setOnClickListener(this);
//mProductImageView.setOnClickListener(this);
scaleUpFooterExtra = AnimationUtils.loadAnimation(mContext, R.anim.scale_up);
scaleDownFooterExtra = AnimationUtils.loadAnimation(mContext, R.anim.scale_down);
}
public void bindProduct(Product product)
{
/* Inicializamos los TextViews */
mTitleTextView.setText(product.getShop());
mSubtitleTextView.setText(product.getColors().get(0).getReference());
mNameTextView.setText(product.getName());
mPriceTextView.setText(String.format("%.2f", product.getPrice()) + "€");
/* Ocultamos la info, IMPORTANTE. Cosas malas pasan si no se pone. Tambien la imagen de error. */
mProductFooterExtraView.setVisibility(View.GONE);
mProductFooterMainView.setVisibility(View.GONE);
mErrorImageView.setVisibility(View.GONE);
/* Mostramos la view de carga y el background */
mLoadingView.setVisibility(View.VISIBLE);
mBackgroundView.setVisibility(View.VISIBLE);
/* Ponemos el icono del corazon. */
mFavImageButton.setBackgroundResource(R.drawable.ic_favorite_border_white);
/* Cargamos la imagen usando Picasso */
String url = product.getColors().get(0).getImages().get(0).getPath().replaceAll(".jpg", "_Small.jpg");
Picasso.with(mContext)
.load(url)
.into(mProductImageView, new Callback() {
#Override
public void onSuccess() {
mBackgroundView.setVisibility(View.GONE);
mLoadingView.setVisibility(View.GONE);
mProductFooterMainView.setVisibility(View.VISIBLE);
}
#Override
public void onError() {
mLoadingView.setVisibility(View.GONE);
mErrorImageView.setVisibility(View.VISIBLE);
}
});
mProduct = product;
}
public ProductsGridAdapter(Context context, List<Product> productList)
{
mContext = context;
mProductList = productList;
}
public void updateProductList(List<Product> productList)
{
mProductList = productList;
}
#Override
public ProductHolder onCreateViewHolder(ViewGroup viewGroup, int viewType)
{
View itemView = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.product_item_grid
, viewGroup
, false );
return new ProductHolder(itemView);
}
#Override
public void onBindViewHolder(final ProductHolder productHolder, int pos)
{
productHolder.bindProduct(mProductList.get(pos));
}
#Override
public int getItemCount()
{
return mProductList.size();
}
}
EDIT 2: I've found out what's going on. The cardview is firstly created without the image, when it's loaded, the layout is redrawn, therefore the weird movement produced. I tried to do this:
WHen the image is first loaded, I save the height of it, and when the recyclerView is scrolled up, I set the background image of the cardview with the height already saved so it has the same dimensions as the image, and this should do the trick. But it doesnt. This is what I tried, this code belongs to the Holder constructor in the Adapter.
final ViewTreeObserver mProductImageViewTreeObserver = mProductImageView.getViewTreeObserver();
final ViewTreeObserver mBackgroundTreeObserver = mBackgroundView.getViewTreeObserver();
mProductImageViewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
if (mProductHeight != mProductImageView.getHeight())
{
mProductHeight = mProductImageView.getHeight();
}
}
});
mBackgroundTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
if((mProductHeight > 0) && (mProductHeight != mBackgroundView.getHeight()))
{
mBackgroundView.getLayoutParams().height = mProductHeight;
}
return true;
}
});
Does anyone have a clue of what's wrong?
Thanks in advance,

I found the solution myself. The trick is to save the aspect ratio when loading the image for the first time into a Target object.
Then, in the OnPrepareLoad, check if this image has been loaded previously. If so, use the aspect ratio stored to set the final height, this way, there's no readjustment and the scroll works smoothly.
mTarget = new Target()
{
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from)
{
mLoadingView.setVisibility(View.GONE);
mProductFooterMainView.setVisibility(View.VISIBLE);
mProductImageView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
mProductImageView.setBackgroundColor(-1);
mProductImageView.setAlpha(1.0f);
if (mProductBitmapArray[position] == 0.0f)
mProductBitmapArray[position] = (double)bitmap.getHeight() / (double)bitmap.getWidth();
mProductImageView.setImageBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable)
{
... do stuff
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable)
{
mProductImageView.setImageBitmap(null);
if (mProductBitmapArray[position] != 0)
mProductImageView.getLayoutParams().height = (int)(mProductImageView.getWidth()
* mProductBitmapArray[position]);
else
mProductImageView.getLayoutParams().height = 600;
mProductImageView.setBackgroundColor(mContext.getResources().getColor(R.color.colorText));
mProductImageView.setAlpha(0.1f);
}
};

Related

Android List Adapter not showing all data using Live Data, ViewHolder and ViewModelProvider

I am following this tutorial and adapting things to my project on the go: https://codelabs.developers.google.com/codelabs/android-training-livedata-viewmodel/index.html?index=..%2F..android-training#0
I have a database with conversation lists which I want to show in an activity, and clicking them should get me to conversation screen. For example 3 conversations should appear exactly like the whatsapp main screen, the problem is that even tough there are more entries of conversations history that I can see in the conversations variable, only one is shown in the recycle view.
Activity code:
public class activity_conversations_list extends AppCompatActivity {
private ChatViewModel chatViewModel;
public void startConversation(View view) {
//click on conversation
Intent intent = new Intent(this, activity_conversation.class);
startActivity(intent);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_conversations_list);
RecyclerView recyclerView = findViewById(R.id.recyclerViewConversations);
final ConversationListAdapter adapter = new ConversationListAdapter(this);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
chatViewModel = ViewModelProviders.of(this).get(ChatViewModel.class);
chatViewModel.getConversations().observe(this, new Observer<List<Conversation>>() {
#Override
public void onChanged(#Nullable final List<Conversation> conversations) {
// Update the cached copy of the words in the adapter.
adapter.setConversations(conversations);
}
});
}
}
xml for conversation list activity
<?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:layout_height="match_parent"
tools:context=".activity_conversations_list">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerViewConversations"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
</LinearLayout>
List adapter class:
public class ConversationListAdapter
extends RecyclerView.Adapter<ConversationListAdapter.ConversationItemViewHolder> {
//conversations list
private List<Conversation> conversations;
private final LayoutInflater layoutInflater;
public ConversationListAdapter(Context context) { layoutInflater = LayoutInflater.from(context); }
#Override
public ConversationItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = layoutInflater.inflate(R.layout.view_conversation_list_item, parent, false);
return new ConversationItemViewHolder(itemView);
}
#Override
public void onBindViewHolder(ConversationItemViewHolder holder, int position) {
if (conversations != null) {
Conversation current = conversations.get(position);
holder.textViewConversationName.setText(current.getConversation_name());
} else {
// Covers the case of data not being ready yet.
holder.textViewConversationName.setText("No conversations");
}
}
public void setConversations(List<Conversation> conversations){
this.conversations = conversations;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
if (conversations != null)
return conversations.size();
else return 0;
}
class ConversationItemViewHolder extends RecyclerView.ViewHolder {
private final TextView textViewConversationName;
private ConversationItemViewHolder(View itemView) {
super(itemView);
textViewConversationName = itemView.findViewById(R.id.textViewConversationName);
}
}
}
xml for list item:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/linearLayoutConversationListItem"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textViewConversationName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:clickable="true"
android:focusable="auto"
android:onClick="startConversation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
change your xml for list item
to the one below
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/linearLayoutConversationListItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/textViewConversationName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:clickable="true"
android:focusable="auto"
android:onClick="startConversation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Your items are there, just your item fills 1 full screen, if you start scrolling you will see it is there.

Clickable area of recyclerview not 100% width until scroll

I'm developing a screen for an android app where I need to add a click event to each row of recyclerview and it's working in parts.
The problem is that once the intent is loaded the clickable area is limited to the width of its the contents.
the clickable area works fine (at 100% width) only if the recyclerview row is completely hidden from the viewport after a scroll happen.
Bellow has a gif that illustrates my problem. Note that before scroll the clickable area is reduced to row content and after scroll the clickable area fills the row.
<img src="https://i.imgur.com/wGvqa5Z.gif" width="150" style="width:150px; border:1px solid #000;" />
active_catalog.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="viniciusdesouza.com.br.barbearia.CatalogActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar_catalog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:minHeight="?attr/actionBarSize"
android:theme="#style/ThemeOverlay.AppCompat.Dark" />
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_catalog" />
</android.support.design.widget.CoordinatorLayout>
content_catalog.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="viniciusdesouza.com.br.barbearia.CatalogActivity"
tools:showIn="#layout/activity_catalog">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_catalog_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
catalog_item.xml
<?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="72sp"
android:gravity="center_vertical"
android:orientation="vertical"
android:background="?android:attr/selectableItemBackground">
<TextView
android:id="#+id/txt_catalog_item_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="TextView"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="#+id/txt_catalog_item_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="TextView" />
</LinearLayout>
CatalogAdapter.java
public class CatalogAdapter extends RecyclerView.Adapter<CatalogAdapter.ViewHolderCatalog> {
private List<CatalogItem> catalog;
private OnRecyclerViewItemClickListener listener;
public CatalogAdapter (List<CatalogItem> catalog) {
this.catalog = catalog;
}
#Override
public CatalogAdapter.ViewHolderCatalog onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.catalog_item, parent, false);
ViewHolderCatalog viewHolderCatalog = new ViewHolderCatalog(view);
return viewHolderCatalog;
}
#Override
public void onBindViewHolder(CatalogAdapter.ViewHolderCatalog holder, final int position) {
if ( (catalog != null) && (catalog.size() > 0)) {
CatalogItem item = catalog.get(position);
holder.txtItemName.setText(item.name);
holder.txtItemPrice.setText(String.valueOf(item.price));
holder.itemView.setOnClickListener(new View.OnClickListener () {
#Override
public void onClick(View v){
listener.onRecyclerViewItemClicked(position, -1);
}
});
}
}
#Override
public int getItemCount() {
return catalog.size();
}
public static class ViewHolderCatalog extends RecyclerView.ViewHolder {
public TextView txtItemName;
public TextView txtItemPrice;
public ViewHolderCatalog(View itemView) {
super(itemView);
txtItemName = (TextView) itemView.findViewById(R.id.txt_catalog_item_name);
txtItemPrice = (TextView) itemView.findViewById(R.id.txt_catalog_item_price);
}
}
/**
* Custom created method for Setting the item click listener for the items and items with in items
* #param listener OnRecyclerViewItemClickListener
*/
public void setOnItemClickListener(OnRecyclerViewItemClickListener listener)
{
this.listener = listener;
}
}
CatalogActivity.java
public class CatalogActivity extends AppCompatActivity implements OnRecyclerViewItemClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_catalog);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_catalog);
setSupportActionBar(toolbar);
// back button
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
recyclerViewCatalog = (RecyclerView) findViewById(R.id.rv_catalog_list);
recyclerViewCatalog.setHasFixedSize(true);
dbConnect();
catalogRepository = new CatalogRepository(databaseHandler);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
catalog = catalogRepository.all();
recyclerViewCatalog.setLayoutManager(linearLayoutManager);
catalogAdapter = new CatalogAdapter(catalog);
mDividerItemDecoration = new DividerItemDecoration(
recyclerViewCatalog.getContext(),
linearLayoutManager.getOrientation()
);
recyclerViewCatalog.addItemDecoration(mDividerItemDecoration);
recyclerViewCatalog.setAdapter(catalogAdapter);
catalogAdapter.setOnItemClickListener(this);
}
#Override
public void onRecyclerViewItemClicked(int position, int id)
{
// do something with item clicked
}
}
Any help will be appreciated
The issue has to do with your ConstraintLayout in your content_catalog.xml file.
A similar issue is happening here, so to fix it, try simply replacing it with something else like a LinearLayout or RelativeLayout and it should resolve the issue of the view not expanding until after scrolling re-renders it.
Just change your ViewHolderCatalog class like below
public static class ViewHolderCatalog extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView txtItemName;
public TextView txtItemPrice;
public ViewHolderCatalog(View itemView) {
super(itemView);
txtItemName = (TextView) itemView.findViewById(R.id.txt_catalog_item_name);
txtItemPrice = (TextView) itemView.findViewById(R.id.txt_catalog_item_price);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), “You clicked your your recycler view item”, Toast.LENGTH_SHORT).show();
}
}

RecyclerView 2 Columns with CardView

I have a problem with my layout. I'm trying to do something like this:
For now, i have a RecyclerView with a CardView inside it. in the CardView I have put an ImageView and a TextView but I don't know why but the CardView is more height than ImageView inside it.
Here is The code and a Sample Image.
And Here is the code: Activity
public class AddRoomActivity extends AppCompatActivity implements View.OnClickListener {
private View snackView;
private FloatingActionButton fabDoneAddRoom;
private EditText etRoomName;
private String roomName = null;
public final static String KEY_PI_IP = "MyPi_IP";
private final static String KEY_ROOM = "myRoom";
private final static String KEY_ROOM_TYPE = "myRoom_Type";
private RecyclerView typeRecyclerView;
private GridLayoutManager layoutManager;
private AddRoomActivity.TypeAdapter adapter;
private String myPi;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_room);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
fabDoneAddRoom = (FloatingActionButton) findViewById(R.id.doneAddRoom);
fabDoneAddRoom.setOnClickListener(this);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
etRoomName = (EditText) findViewById(R.id.addRoomName);
myPi = getIntent().getStringExtra(KEY_PI_IP);
layoutManager = new GridLayoutManager(this, 2);
typeRecyclerView = (RecyclerView) findViewById(R.id.recyclerTypeRoom);
typeRecyclerView.setHasFixedSize(true);
typeRecyclerView.setLayoutManager(layoutManager);
// specify an adapter (see also next example)
adapter = new TypeAdapter(getResources().getStringArray(R.array.roomTypeName));
typeRecyclerView.setAdapter(adapter);
}
void showToastMessage(String message) {
Snackbar.make(snackView, message, Snackbar.LENGTH_LONG).show();
}
#Override
public void onClick(View v) {
if (v.getId() == R.id.doneAddRoom) {
snackView = v;
String myString = etRoomName.getText().toString();
if (myString.length() > 0) {
roomName = myString.substring(0, 1).toUpperCase() + myString.substring(1);
addRoomToPi();
} else {
showToastMessage(getString(R.string.noNameRoom));
}
}
}
private void addRoomToPi() {
Integer ret = -1;
try {
ret = (Integer) new RaspberryTCPClient(myPi, getResources(), RaspberryTCPClient.TYPE_ADD_ROOM, roomName, XMLRoom.TYPE_KITCHEN_ROOM).execute().get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
if (ret == RaspberryTCPClient.OPERATION_DONE) {
showToastMessage(getString(R.string.roomAdded));
Intent data = new Intent();
data.putExtra(KEY_ROOM, roomName);
setResult(Activity.RESULT_OK, data);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
finish();
}
}, 1500);
} else {
showToastMessage(getString(R.string.addRoomError));
}
}
private class TypeAdapter extends RecyclerView.Adapter<AddRoomActivity.TypeAdapter.ViewHolder> {
private String[] myData;
public TypeAdapter(String[] roomList) {
myData = roomList;
}
public void onItemClick(int position) {
}
public class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView tvType;
public CardView cvRoomCard;
public ImageView imgRoomType;
public ViewHolder(View vCard) {
super(vCard);
cvRoomCard = (CardView) vCard;
tvType = (TextView) vCard.findViewById(R.id.tvTypeName);
imgRoomType = (ImageView) vCard.findViewById(R.id.img_roomType);
}
}
#Override
public AddRoomActivity.TypeAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.type_room_recycler_view, parent, false);
// set the view's size, margins, paddings and layout parameters
//...
AddRoomActivity.TypeAdapter.ViewHolder vh = new AddRoomActivity.TypeAdapter.ViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(AddRoomActivity.TypeAdapter.ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
holder.tvType.setText(myData[position]);
switch (position) {
case XMLRoom.TYPE_ROOM:
holder.imgRoomType.setImageResource(R.drawable.img_room_sqr);
break;
case XMLRoom.TYPE_BED_ROOM:
holder.imgRoomType.setImageResource(R.drawable.img_bedroom_sqr);
break;
case XMLRoom.TYPE_GARDEN_ROOM:
holder.imgRoomType.setImageResource(R.drawable.img_garden_sqr);
break;
case XMLRoom.TYPE_KITCHEN_ROOM:
holder.imgRoomType.setImageResource(R.drawable.img_kitchen_sqr);
break;
case XMLRoom.TYPE_LIVING_ROOM:
holder.imgRoomType.setImageResource(R.drawable.img_living_room_sqr);
break;
case XMLRoom.TYPE_SWIMMING_POOL_ROOM:
holder.imgRoomType.setImageResource(R.drawable.img_swimming_pool_sqr);
break;
}
holder.cvRoomCard.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onItemClick(position);
}
});
}
#Override
public int getItemCount() {
return myData.length;
}
}
The MainLayout
<android.support.design.widget.AppBarLayout
android:id="#+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="#dimen/toolbar"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="90dp"
android:layout_marginTop="#dimen/toolbar"
android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true">
<TextView
android:id="#+id/tvAddRoom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_margin="5dp"
android:gravity="center"
android:text="#string/textAddRoom"
android:textColor="#color/primary_text"
android:textSize="20dp"
android:textStyle="bold" />
<android.support.design.widget.TextInputLayout
android:id="#+id/inputaddRoomName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/tvAddRoom"
android:layout_gravity="center"
android:layout_margin="5dp">
<EditText
android:id="#+id/addRoomName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/prompt_RoomName"
android:inputType="textEmailAddress"
android:maxLines="1"
android:singleLine="true" />
</android.support.design.widget.TextInputLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerTypeRoom"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/inputaddRoomName"
android:scrollbars="vertical" />
</RelativeLayout>
<android.support.design.widget.FloatingActionButton
android:id="#+id/doneAddRoom"
android:layout_width="#dimen/fab_Dimension"
android:layout_height="#dimen/fab_Dimension"
android:layout_gravity="bottom|center"
android:layout_margin="#dimen/fab_margin"
app:srcCompat="#drawable/ic_done" />
</android.support.design.widget.CoordinatorLayout>
and The View Layout:
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardCornerRadius="4dp">
<ImageView
android:id="#+id/img_roomType"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:src="#drawable/img_room" />
<TextView
android:id="#+id/tvTypeName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="center"
android:textColor="#android:color/white"
android:textSize="20sp" />
</android.support.v7.widget.CardView>
Extracted required info from the accepted answer in case URL becomes invalid in future and to save time.
GridLayoutManager is used to display the RecyclerView in Grid manner instead of list.
RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(mLayoutManager);
Kotlin version:
recyclerView.apply {
layoutManager = GridLayoutManager(this, 2)
}
You can use this code simply
<android.support.v7.widget.RecyclerView
app:layoutManager="android.support.v7.widget.GridLayoutManager"
app:spanCount="2"/>
With androidX libraries simply do:
<androidx.recyclerview.widget.RecyclerView
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"/>
for Horizontal recycler view, it works for me
layoutManagerSuperCategories = new GridLayoutManager(context,2,LinearLayoutManager.HORIZONTAL,false);
rv_superCategories.setLayoutManager(layoutManagerSuperCategories);
use "0dp" in layout_width and layout_height for putting views in it's position.
like this:
android:layout_width="0dp"
and use Guidelines in your xml.
<ImageView
android:id="#+id/img_roomType"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_alignParentTop="true"
android:src="#drawable/img_room" />
<TextView
android:id="#+id/tvTypeName"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="bottom"
android:gravity="center"
android:textColor="#android:color/white"
android:textSize="20sp" />
this is NOT true code, u have to use Guidelines.

Images in a horizontal recyclerview switces around

I am trying to make a simple horizontal recyclerview with images and some text etc.
But for some reason, the images are switcing around and ending up in the wrong places when I scroll back and forth.
The problem lies, without a doubt in the adapter:
public class SponsoredAdvertsAdapter extends RecyclerView.Adapter<SponsoredAdvertsAdapter.SponsoredAdvertHolder> {
private Context context;
private List<CustomAdvert> adverts;
private boolean isBigScreen;
public SponsoredAdvertsAdapter(Context context, List<CustomAdvert> adverts) {
this.context = context;
this.adverts = adverts;
isBigScreen = ScreenUtil.isBigScreen(context);
}
#Override
public SponsoredAdvertsAdapter.SponsoredAdvertHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_search_popular_adverts_phone, null);
if (isBigScreen) {
layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_search_popular_adverts_tablet, null);
}
return new SponsoredAdvertHolder(layoutView);
}
#Override
public void onBindViewHolder(final SponsoredAdvertHolder holder, int position) {
final CustomAdvert advert = adverts.get(position);
String title = advert.getAdvert().getValidTextShort();
holder.title.setText(title);
holder.unreadBadge.setText(context.getString(R.string.customer_tilbudsaviser_unread_advert));
if (advert.isRead()) {
holder.unreadBadge.setVisibility(View.INVISIBLE);
} else {
holder.unreadBadge.setVisibility(View.VISIBLE);
}
if (holder.image != null) {
attachAdvertImage(holder.image, holder.imageView);
} else {
String logoUrl = ImageScaleUrlBuilder.getFixedWidthUrl(advert.getLogoUrl(), holder.imageView.getMeasuredWidth());
ImageLoaderHelper.loadImageFromUrl(logoUrl, new ImageLoaderHelper.ImageLoadedCallback() {
#Override
public void onBitmapLoaded(Bitmap bitmap) {
holder.image = bitmap;
attachAdvertImage(bitmap, holder.imageView);
}
});
}
AdvertActivity.startAdvertActivity(context, advert.getCustomer(), advert.getAdvert(), 0, null);
}
private void attachAdvertImage(final Bitmap image, final ImageView imageView) {
RunOnUIThread.post(new Runnable() {
#Override
public void run() {
imageView.setImageBitmap(image);
}
});
}
#Override
public int getItemCount() {
return adverts.size();
}
public class SponsoredAdvertHolder extends RecyclerView.ViewHolder {
private final View container;
private final ImageView imageView;
private final TextView title, unreadBadge;
private Bitmap image;
public SponsoredAdvertHolder(View itemView) {
super(itemView);
container = itemView.findViewById(R.id.item_search_popular_adverts_container);
imageView = (ImageView) itemView.findViewById(R.id.search_popular_advert_imageview);
title = (TextView) itemView.findViewById(R.id.search_popular_advert_title);
unreadBadge = (TextView) itemView.findViewById(R.id.unread_badge);
}
}
}
Here is the code where I set the adapter to the recyclerview:
sponsoredAdvertsAdapter = new SponsoredAdvertsAdapter(getContext(), adverts);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);
sponsoredAdvertsRecyclerView.setLayoutManager(linearLayoutManager);
sponsoredAdvertsRecyclerView.setAdapter(sponsoredAdvertsAdapter);
And here is the recyclerview (xml):
<LinearLayout
android:id="#+id/popular_searches_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1"
android:background="#color/white">
<android.support.v7.widget.RecyclerView
android:id="#+id/popular_adverts_searches_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
And the xml for the item:¨
<?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:layout_height="match_parent"
android:padding="8dp"
android:id="#+id/item_search_popular_adverts_container">
<ImageView
android:id="#+id/search_popular_advert_imageview"
android:layout_width="136dp"
android:layout_height="170dp"
android:gravity="bottom"
android:scaleType="fitXY"
android:layout_marginBottom="10dp"
android:layout_gravity="center_horizontal"/>
<TextView
android:id="#+id/search_popular_advert_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textColor="#color/secondary_grey"
android:textSize="14sp"
android:text="Title"/>
<include
android:id="#+id/unread_badge"
layout="#layout/item_badge"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:text="#string/customer_tilbudsaviser_unread_advert"
android:visibility="invisible" />
</LinearLayout>
I know that this is a fairly common problem with Recyclerviews, but even after looking at other solutions, I can't seem to get my solution to work.
Im suggesting you to use an image library like Glide (git link) to display with image view. it will cache the images properly and show. this may fix the issue ;)
use glide by replacing this line
imageView.setImageBitmap(image);
to
Glide
.with(myFragment)
.load(bitmap)
.centerCrop()
.crossFade()
.into(myImageView);

Android RecyclerView Blank Space

I have an android project and I am using Recycler view to print a list with items, each item has just one image and between those images, android is creating a big blank space. I researched and saw about to change my layout to wrap_content and I changed it the recycler view layout, the itemlayout, the layout of the page but didn't work, I want to know what more options do I have to change, I will post here my 2 classes, the activity, and the adapter, and both layout XML and the item XML (Recycler viewItem )
ACTIVITY:
public class MenuPrincipal extends AppCompatActivity {
private RecyclerView recyclerView;
private MenuEsportesAdapter adapterGrupos;
private ArrayList<MenuEsporte> listaGruposMenu;
private ImageView imgTopo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu_principal);
imgTopo = (ImageView) findViewById(R.id.MenuPrincipal_imagemTopo);
imgTopo.setImageResource(R.drawable.sol);
MenuEsporte me = new MenuEsporte();
listaGruposMenu = me.getAllEsportes();
recyclerView = (RecyclerView) findViewById(R.id.recyclerViewMenuGrupo);
recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setHasFixedSize(false);
adapterGrupos = new MenuEsportesAdapter(this, listaGruposMenu, onClickGruposMenu());
recyclerView.setAdapter(adapterGrupos);
}
private MenuEsportesAdapter.MenuEsporteOnClickListener onClickGruposMenu(){
return new MenuEsportesAdapter.MenuEsporteOnClickListener(){
#Override
public void onClickMenuEsporte(MenuEsportesAdapter.MenuEsportesViewHolder holder, int idx) {
Toast.makeText(getApplicationContext(),"Clicou " + idx, Toast.LENGTH_SHORT).show();
Intent i = new Intent(getApplicationContext(),JogosClassificacao.class);
Bundle bundle = new Bundle();
bundle.putString("Esporte", listaGruposMenu.get(idx).nomeEsporte);
i.putExtras(bundle);
startActivity(i);
}
};
}
}
ADAPTER
public class MenuEsportesAdapter extends RecyclerView.Adapter<MenuEsportesAdapter.MenuEsportesViewHolder> {
protected static final String TAG = "livroandroid";
private final List<MenuEsporte> listaMenuEsporte;
private final Context context;
private final MenuEsporteOnClickListener onClickListener;
public interface MenuEsporteOnClickListener {
public void onClickMenuEsporte(MenuEsportesViewHolder holder, int idx);
}
public MenuEsportesAdapter(Context context, List<MenuEsporte> listaMenuEsporte, MenuEsporteOnClickListener onClickListener) {
this.context = context;
this.listaMenuEsporte = listaMenuEsporte;
this.onClickListener = onClickListener;
}
#Override
public MenuEsportesViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Este método cria uma subclasse de RecyclerView.ViewHolder
// Infla a view do layout
View view = LayoutInflater.from(context).inflate(R.layout.lista_item, viewGroup, false);
// Cria a classe do ViewHolder
MenuEsportesViewHolder holder = new MenuEsportesViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(final MenuEsportesViewHolder holder, final int position) {
// Este método recebe o índice do elemento, e atualiza as views que estão dentro do ViewHolder
MenuEsporte c = listaMenuEsporte.get(position);
// Atualizada os valores nas views
//holder.tNome.setText(c.nomeEsporte);
holder.img.setImageResource(c.fotoResource);
//holder.img.setImageURI(Uri.fromFile(file));
// Click
if (onClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Chama o listener para informar que clicou no Planeta
onClickListener.onClickMenuEsporte(holder, position);
}
});
}
}
#Override
public int getItemCount() {
return this.listaMenuEsporte != null ? this.listaMenuEsporte.size() : 0;
}
// Subclasse de RecyclerView.ViewHolder. Contém todas as views.
public static class MenuEsportesViewHolder extends RecyclerView.ViewHolder {
public TextView tNome;
ImageView img;
private View view;
public MenuEsportesViewHolder(View view) {
super(view);
this.view = view;
// Cria as views para salvar no ViewHolder
//tNome = (TextView) view.findViewById(R.id.textViewNome);
img = (ImageView) view.findViewById(R.id.imageViewFoto);
}
}
}
And the xmls
<?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:layout_height="wrap_content">
<!--
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#000000"
android:id="#+id/textViewNome"/>
-->
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/imageViewFoto"/>
</LinearLayout>
Second:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="juriteam.br.com.thr_sportgames.paginas.MenuPrincipal"
tools:showIn="#layout/activity_menu_principal"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="2"
android:id="#+id/MenuPrincipal_imagemTopo"
android:layout_gravity="center_horizontal" />
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerViewMenuGrupo"
android:layout_height="0dp"
android:layout_weight="7"
android:layout_width="match_parent"/>
</LinearLayout>
Can someone please help me ?
Sorry about the names and comments in portuguese. Any question please ask me.
Thank you so much
Problem is with RecyclerView item layout. You have given match_parent property to ImageView. Instead of this give constant size like 48dp.
In the lista_item layout, you should set the height to wrap_content or a constant size.
You have to specify the height of your ImageView in your lista_item instead of setting the height to match_parent

Categories

Resources