Can anyone please please help me
I want to create a recycler view with 1st row scrolling horizontally and the rest should be scrolling vertically just like instagram home page.
I have looked up almost everything but no luck.
Please can anyone help.
In your adapter class
private static final int POSTER = 1; //for sliding item
private static final int CHILDGROUP = 2; //normal items
In getItemViewType()
#Override
public int getItemViewType(int position) {
if (position == 0 )
return POSTER;
else
return CHILDGROUP;
}
In onCreateViewHolder() check which item & inflate layout as per item
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == POSTER) {
View view = LayoutInflater.from(mContext).inflate(R.layout.listrow_auto_viewpager, parent, false);
return new PosterSliderHolder(view);
} else {
View view = LayoutInflater.from(mContext).inflate(R.layout.listrow_sub_category, parent, false);
return new GroupViewHolder(view);
}
}
In onBindViewHolder() check the item type & load the data
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder.getItemViewType() == POSTER) {
final PosterSliderHolder posterViewHolder = (PosterSliderHolder) holder;
//your logic for sliding item (horizontal recyclerview )
}
else{
final GroupViewHolder groupViewHolder = (GroupViewHolder) holder;
//normal list item
}
}
Related
I have a simple mp3 player based on RecyclerView.
There is an Adapter and ViewHolder for handling tracks in playlist.
public class TracksAdapter extends RecyclerView.Adapter<TracksAdapter.ViewHolder> {
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cell_playlist, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.trackName.setText("Track " + position);
}
#Override
public int getItemCount() {
return tracks.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
final TextView trackName;
ViewHolder(View itemView) {
super(itemView);
trackName = itemView.findViewById(R.id.trackName);
}
}
}
When I click on track I want to change background color of selected track in playlist. Problem is when I set background color to one item it's will be affected to every tenth cell in playlist.
For example if there are 100 tracks in the playlist, 10 tracks will be highlighted including the selected.
Method findViewHolderForAdapterPosition(position) returns ViewHolder with selected track.
private void setTrackColor(int position) {
RecyclerView.ViewHolder holder =
recyclerView.findViewHolderForAdapterPosition(position);
if (holder == null) return;
View item = holder.itemView;
item.setBackgroundColor(ContextCompat.getColor(main, R.color.playing));
}
You can't change the color of a view direcly. In a RecyclerView, all the Views are re-used. So, if you change the color in a position, you may indirecly change the color in other positions because same view will be re-used.
You must save the position of the currently playing track separately. This way, during onBindViewHolder, you check if current view being bind is the track currently playing. If it is the same track, apply some color. If it is not the same color, restore default color
public class TracksAdapter extends RecyclerView.Adapter<TracksAdapter.ViewHolder> {
private int mTrackPlaying = -1;
public void setTrackPlaying(int position) {
mTrackPlaying = position;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.trackName.setText("Track " + position);
if(position == mTrackPlaying) {
holder.itemView.setBackgroundColor(ContextCompat.getColor(main, R.color.playing));
} else {
// Here, you must restore the color because the view is reused.. so, you may receive a reused view with wrong colors
holder.itemView.setBackgroundColor(ContextCompat.getColor(main, R.color.NOT_playing));
}
}
}
And then
private void setTrackColor(int position) {
TracksAdapter adapter = (TracksAdapter) recyclerView.getAdapter();
adapter.setTrackPlaying(position);
// Line below will `RecyclerView` to re-draw that position.. in other words, it will triggers a call to `onBindViewHolder`
adapter.notifyItemChanged(position);
// Reset the color of song previously playing..
adapter.notifyItemChanged(oldPosition);
}
I have RecyclerViewAdapter with 2 item types. ItemViewHolder for all itemViews in ArrayList and HeaderViewHolder for one headerView. I can remove item from ArrayList and then use notifyItemRemoved(position) in order to remove itemView from RecyclerView. But how do I remove headerView which is not associated with the ArrayList?
Below is some code from RecyclerViewAdapter:
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private List<Offer> mValues;
OfferListAdapter(List<String> items) {
mValues = items;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.itemView, parent, false);
return new ItemViewHolder(v);
} else if (viewType == TYPE_HEADER) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.headerView, parent, false);
return new HeaderViewHolder(v);
}
return null;
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
if (holder instanceof ItemViewHolder) {
ItemViewHolder userViewHolder = (ItemViewHolder) holder;
} else if (holder instanceof HeaderViewHolder) {
HeaderViewHolder headerViewHolder= (HeaderViewHolder) holder;
}
}
#Override
public int getItemViewType(int position) {
if (isPositionHeader(position))
return TYPE_HEADER;
return TYPE_ITEM;
}
private boolean isPositionHeader(int position) {
return position == 0;
}
private Offer getItem(int position) {
return mValues.get(position - 1);
}
#Override
public int getItemCount() {
return mValues.size() + 1;
}
The HeaderView is part of your list, at least from the adapters point of view.
You say isPositionHeader(pos -> pos == 0) to indicate that the first item of your list is the header view, and you tell the adapter your list has mValues.size() + 1 items in it. The +1 to make up for the header which comes first.
So how could you remove this view again?
If you want to toggle it on / off...you need something that can toggle between states. Why not use a boolean? You could have some isShowingHeader field that indicates whether or not the header gets displayed.
How would this affect your code? Well...the same code as above...
// if we show the header, the 0 position is the header
isPositionHeader(pos -> isShowingHeader && pos == 0)
And for your list size...
// list is longer by 1 when showing a header
mValues.size() + (isShowingHeader ? 1 : 0)
When showing / hiding your header you now only have to update isShowingHeader and call notifyDataSetChanged() to notify the adapter of your changed values.
I am trying to display detailed Product information in an custom Listview with two TextViews per row for the key/value pair. The data is displayed correct. I also colored every second line different.
And there is my Problem. If I scroll up and down the different colored rows change their color and remain in this state. The data is not affected from this problem. Just the backroundcolor of the TextViews. I use the ViewHolder Pattern but this did not change anything. I added the code of the adapter. I think thats enough. Have you any idea?
Screenshot of the problem:
Code:
public class ProductDetailAdapter extends BaseAdapter {
private LinkedHashMap<String,String> list;
private Context context;
public ProductDetailAdapter(Context c, LinkedHashMap<String,String> list){
super();
this.context = c;
this.list=list;
}
#Override
public int getCount() {
return list.size();
}
#Override
public Object getItem(int position) {
return list.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ProductDetailAdapter.ViewHolder viewHolder;
if(convertView == null){
convertView=inflater.inflate(R.layout.product_detail_data_row,null);
viewHolder = new ViewHolder();
viewHolder.textViewKey = (TextView) convertView.findViewById(R.id.productDataKey);
viewHolder.textViewValue = (TextView) convertView.findViewById(R.id.productDataValue);
convertView.setTag(viewHolder);
}else {
viewHolder = (ProductDetailAdapter.ViewHolder) convertView.getTag();
}
viewHolder.textViewKey.setText((String)list.keySet().toArray()[position]);
viewHolder.textViewValue.setText(list.get(list.keySet().toArray()[position]));
if(position % 2 == 0){
viewHolder.textViewKey.setBackgroundColor(context.getResources().getColor(R.color.colorParkerWhite2));
viewHolder.textViewValue.setBackgroundColor(context.getResources().getColor(R.color.colorParkerWhite2));
}
return convertView;
}
private static class ViewHolder {
public TextView textViewKey;
public TextView textViewValue;
public ViewHolder(){};
}
}
That happens because the rows are being recycled. It's a common problem.
You can solve it by doing:
if(position % 2 == 0){
viewHolder.textViewKey.setBackgroundColor(context.getResources().getColor(R.color.colorParkerWhite2));
viewHolder.textViewValue.setBackgroundColor(context.getResources().getColor(R.color.colorParkerWhite2));
} else {
viewHolder.textViewKey.setBackgroundColor(context.getResources().getColor(R.color.colorParkerWhite1)); //Or the color that you want for odd rows
viewHolder.textViewValue.setBackgroundColor(context.getResources().getColor(R.color.colorParkerWhite1)); //Or the color that you want for odd rows
}
Try this:
super( c, 0, list );
instead of this:
super();
Once you pass the data source to the adapter you no longer need :
getCount
getItem
getItemId
please refer this link and link2 and link3 these will work for you
I am using the following code in Adapter class to show data in a RecyclerView, but now I would like to show data from some other ArrayList in a same RecyclerView (at some positions like: 1st position and 6th position) using different layout.
That different layout (assume: another_layout.xml) contains 2 TextViews and an Image, also want to implement click on listener for that layout too..
#Override
public PlaylistCardAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// inflate a card layout
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.youtube_video_card, parent, false);
// populate the viewholder
ViewHolder vh = new ViewHolder(v);
return vh;
}
RecyclerView for more than one layout
1. Override getItemViewType(int position) method
e.g I have two layouts layout1 and layout2.I want layout1 at the top and then layout2. So getItemViewType would be something like this
#Override
public int getItemViewType(int position) {
if(position == 0){ //for layout1
return 1;
}else{
return 2; //for layout2
}
}
2. Different viewholder for each layout like this
class ViewHolder_LayoutOne extends RecyclerView.ViewHolder{
TextView name;
//Constructor
}
class ViewHolder_LayoutTwo extends RecyclerView.ViewHolder{
TextView name;
//Constructor
}
3. Inflate different layouts according to the position
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder vh = null;
if(viewType == 1){
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_layout_one,parent,false);
vh = new MyViewHolder_LayoutOne(view);
}else if(viewType == 2){
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_layout_two,parent,false);
vh = new MyViewHolder_LayoutTwo(view);
}
return vh;
}
4. Bind your views as per the position
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
switch(getItemViewType(position)){
case 1: //for layout 1
((ViewHolder_LayoutOne)holder).name.setText("");
break;
case 2:// for layout 2
((ViewHolder_LayoutTwo)holder).name.setText("");
break;
}
}
5.Now Most important getItemCount() method //return the number of views
#Override
public int getItemCount() {
return toptitles.length + 1;
// as i have only layout at the top and the remaining size equals the length of the array toptitles.So the overall length would be
//number of views of layout1 + number of views of layout2
}
Hope this helps!!!
you can override getItemViewType, and use different ViewHolder for different layout.
You need to override getItemViewType like this :
#Override
public int getItemViewType(int position) {
Add your condition and return viewType(Constant)
}
Now, Check for viewType and retrun viewHolder accordingly.
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEWHOLDER1) {
View v = LayoutInflater.from(activity).inflate(R.layout.layout1, parent, false);
return new ViewHolder1(v);
}else if(viewType == VIEWHOLDER2){
View v = LayoutInflater.from(activity).inflate(R.layout.layout2, parent, false);
return new ViewHolder2(v);
}
}
Now, Check for instance of viewHolder
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof ViewHolder1) {
Show values in UI accordingly.
}else if(holder instanceof ViewHolder2){
Show values in UI accordingly.
}
}
You can set the type you want in getItemViewType(int position) with the layout you want. And onCreateViewHolder you can check viewType and inflate the row accordingly.
here is an example.
I'm attempting to create a Gridview of icons with the Imageview set to one of two images like in my example below:
I'm retrieving two values from the sqlite content provider, one for the number of stamps and the other for the max number of stamps.
I use a custom adapter with a view holder pattern so I attempt to set the model onLoadFinished() in the Fragment. The model I've currently tried is an int[] with either value of 0 or 1 in the values to determine if green stamp is used or gray. This isn't working however as nothing is being displayed in the GridView.
Here's the code snippet from onLoadFinished() from the Fragment:
imageSourceModel = new int[data.getInt(data.getColumnIndex(RewardsEntry.COLUMN_REW_MAX_POINTS))];
int i = data.getInt(data.getColumnIndex(RewardsEntry.COLUMN_REW_POINTS));
for (int j = 0; j < imageSourceModel.length; j++, i--) {
if (i > 0) {
imageSourceModel[j] = 1;
} else {
imageSourceModel[j] = 0;
}
}
stampGridAdapter.setModel(imageSourceModel);
and the code from the custom Adapter:
#Override
public View getView(int i, View view, ViewGroup parent) {
ViewHolder viewHolder;
if (view == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.list_item_discover, parent, false);
viewHolder = new ViewHolder(view);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
if (model[i] > 0) {
viewHolder.stampView.setImageResource(R.drawable.ic_approved_stamp);
} else {
viewHolder.stampView.setImageResource(R.drawable.ic_approved_stamp_disabled);
}
return view;
}
And simple ViewHolder:
private class ViewHolder {
public ImageView stampView;
public ViewHolder(View view) {
stampView = (ImageView) view.findViewById(R.id.grid_stamp_image);
}
}
If I had to guess, I would say that it fails because of the creation order such that getView() is completed before onLoadFinished() can pass the model in. I'm just not sure how to work around this or what other methods I could use. Any help is appreciated.
For what you want to achieve, this seems unnecessary complicated to me, especially the double variable loop. To cut it simple I'd just do this:
in onLoadFinished()
int maxPoints = data.getInt(data.getColumnIndex(RewardsEntry.COLUMN_REW_MAX_POINTS));
int currentPoints = data.getInt(data.getColumnIndex(RewardsEntry.COLUMN_REW_POINTS));
mGridView.setAdapter(new RewardPointAdapter(getActivity(), currentPoints, maxPoints);
your adapter could look like this:
public class RewardPointAdapter extends BaseAdapter {
private LayoutInflater mLayoutInflater;
private int mMaxRewardPoints;
private int mCurrentRewardPoints;
public RewardPointAdapter(Context context, int currentRewardPoints, int maxRewardPoints) {
mLayoutInflater = LayoutInflater.from(context);
mMaxRewardPoints = maxRewardPoints;
mCurrentRewardPoints = currentRewardPoints;
}
#Override
public int getCount() {
return mMaxRewardPoints;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.list_item_discover, parent, false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if (position < mCurrentRewardPoints) {
viewHolder.stampView.setImageResource(R.drawable.ic_approved_stamp);
} else {
viewHolder.stampView.setImageResource(R.drawable.ic_approved_stamp_disabled);
}
return convertView;
}
private class ViewHolder {
public ImageView stampView;
public ViewHolder(View view) {
stampView = (ImageView) view.findViewById(R.id.grid_stamp_image);
}
}
}