RecyclerView.Adapter Unwanted Space - android

So I want to remove the unwanted space between (sightseeing and shopping) and (my top image and sightseeing).
Sightseeing,shopping,food&dink,hotes are all items of my adapter.
But for some reason my first item has this "space" problem.
Here is my xml for items
<com.inthessaloniki.cityguide.view.SelectorRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment_poi_list_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:listSelector="#drawable/selector_clickable_item_bg_inverse"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="#+id/fragment_poi_list_item_image"
android:layout_width="match_parent"
android:layout_height="80dp"
android:scaleType="fitXY"/>
</RelativeLayout>
</com.inthessaloniki.cityguide.view.SelectorRelativeLayout>
My xml for the list (in this layout)
<FrameLayout
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="match_parent"
android:animateLayoutChanges="true">
<LinearLayout
android:id="#+id/container_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:src="#drawable/home_banner"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/fragment_poi_list_recycler"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbars="vertical"
android:background="#color/global_bg_front"/>
<!--
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="New Button"
android:id="#+id/button"
android:background="#drawable/banner"
android:layout_gravity="center_horizontal"/>-->
<com.google.android.gms.ads.AdView
android:id="#+id/fragment_poi_list_adview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:adUnitId="#string/admob_unit_id_poi_list"
app:adSize="BANNER" />
</LinearLayout>
<include
layout="#layout/placeholder_progress"
android:id="#+id/container_progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<include
layout="#layout/placeholder_offline"
android:id="#+id/container_offline"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<include
layout="#layout/placeholder_empty"
android:id="#+id/container_empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</FrameLayout>
My Adapter Code
public class PoiListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
{
public static final int VIEW_TYPE_POI = 1;
public static final int VIEW_TYPE_IMAGE_FOOTER = 2;
public static final int VIEW_TYPE_FOOTER = 3;
private List<PoiModel> mPoiList;
private List<Object> mFooterList;
private PoiViewHolder.OnItemClickListener mListener;
private int mGridSpanCount;
private Location mLocation;
private boolean mAnimationEnabled = true;
private int mAnimationPosition = -1;
private ImageLoader mImageLoader = ImageLoader.getInstance();
private DisplayImageOptions mDisplayImageOptions;
private ImageLoadingListener mImageLoadingListener;
public PoiListAdapter(List<PoiModel> poiList, List<Object> footerList, PoiViewHolder.OnItemClickListener listener, int gridSpanCount, Location location)
{
mPoiList = poiList;
mFooterList = footerList;
mListener = listener;
mGridSpanCount = gridSpanCount;
mLocation = location;
// image caching options
mDisplayImageOptions = new DisplayImageOptions.Builder()
.showImageOnLoading(android.R.color.transparent)
.showImageForEmptyUri(R.drawable.placeholder_photo)
.showImageOnFail(R.drawable.placeholder_photo)
.cacheInMemory(true)
.cacheOnDisk(true)
.displayer(new SimpleBitmapDisplayer())
.build();
mImageLoadingListener = new AnimateImageLoadingListener();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
// inflate view and create view holder
if(viewType== VIEW_TYPE_POI)
{
View view = inflater.inflate(R.layout.fragment_poi_list_item, parent, false);
return new PoiViewHolder(view, mListener, mImageLoader, mDisplayImageOptions, mImageLoadingListener);
}
else if(viewType==VIEW_TYPE_IMAGE_FOOTER)
{
View view = inflater.inflate(R.layout.footer_image_layout, parent, false);
return new ImageFooterViewHolder(view, mListener);
}
else if(viewType==VIEW_TYPE_FOOTER)
{
View view = inflater.inflate(R.layout.fragment_poi_list_footer, parent, false);
return new FooterViewHolder(view);
}
else
{
throw new RuntimeException("There is no view type that matches the type " + viewType);
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position)
{
// bind data
if(viewHolder instanceof PoiViewHolder)
{
// entity
PoiModel poi = mPoiList.get(getPoiPosition(position));
// render view
if(poi != null)
{
((PoiViewHolder) viewHolder).bindData(poi, mLocation);
}
}
else if(viewHolder instanceof FooterViewHolder)
{
// entity
Object object = mFooterList.get(getFooterPosition(position));
// render view
if(object != null)
{
((FooterViewHolder) viewHolder).bindData(object);
}
}
// set item margins
setItemMargins(viewHolder.itemView, position);
// set animation
setAnimation(viewHolder.itemView, position);
}
#Override
public int getItemCount()
{
int size = 0;
if(mPoiList !=null) size += mPoiList.size();
if(mFooterList!=null) size += mFooterList.size();
if(mFooterList!=null) size += 1; //ImageFooter
return size;
}
#Override
public int getItemViewType(int position)
{
int pois = mPoiList.size();
int footers = mFooterList.size();
int imageFooter = 1;
if(position < pois) return VIEW_TYPE_POI;
else if(position < pois+imageFooter) return VIEW_TYPE_IMAGE_FOOTER;
else if(position < pois+imageFooter+footers) return VIEW_TYPE_FOOTER;
else return -1;
}
public int getPoiCount()
{
if(mPoiList !=null) return mPoiList.size();
return 0;
}
public int getFooterCount()
{
if(mFooterList!=null) return mFooterList.size();
return 0;
}
public int getPoiPosition(int recyclerPosition)
{
return recyclerPosition;
}
public int getFooterPosition(int recyclerPosition)
{
return recyclerPosition - getPoiCount();
}
public int getRecyclerPositionByPoi(int poiPosition)
{
return poiPosition;
}
public int getRecyclerPositionByFooter(int footerPosition)
{
return footerPosition + getPoiCount();
}
public void refill(List<PoiModel> poiList, List<Object> footerList, PoiViewHolder.OnItemClickListener listener, int gridSpanCount, Location location)
{
mPoiList = poiList;
mFooterList = footerList;
mListener = listener;
mGridSpanCount = gridSpanCount;
mLocation = location;
notifyDataSetChanged();
}
public void stop()
{
}
public void setLocation(Location location)
{
mLocation = location;
}
public void setAnimationEnabled(boolean animationEnabled)
{
mAnimationEnabled = animationEnabled;
}
private void setAnimation(final View view, int position)
{
if(mAnimationEnabled && position>mAnimationPosition)
{
view.setScaleX(0f);
view.setScaleY(0f);
view.animate()
.scaleX(1f)
.scaleY(1f)
.setDuration(300)
.setInterpolator(new DecelerateInterpolator());
mAnimationPosition = position;
}
}
private void setItemMargins(View view, int position)
{
int height = (int) CityGuideApplication.getContext().getResources().getDimension(R.dimen.fragment_poi_list_recycler_item_size);
int marginTop = 0;
if(position<mGridSpanCount)
{
TypedArray a = CityGuideApplication.getContext().obtainStyledAttributes(null, new int[]{android.R.attr.actionBarSize}, 0, 0);
marginTop = (int) a.getDimension(0, 0);
a.recycle();
height += marginTop;
}
ViewGroup.MarginLayoutParams marginParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
marginParams.setMargins(0, marginTop, 0, 0);
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.height = height;
}
public static final class PoiViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
{
private TextView nameTextView;
private TextView distanceTextView;
private ImageView imageView;
private OnItemClickListener mListener;
private ImageLoader mImageLoader;
private DisplayImageOptions mDisplayImageOptions;
private ImageLoadingListener mImageLoadingListener;
public interface OnItemClickListener
{
public void onItemClick(View view, int position, long id, int viewType);
}
public PoiViewHolder(View itemView, OnItemClickListener listener, ImageLoader imageLoader, DisplayImageOptions displayImageOptions, ImageLoadingListener imageLoadingListener)
{
super(itemView);
mListener = listener;
mImageLoader = imageLoader;
mDisplayImageOptions = displayImageOptions;
mImageLoadingListener = imageLoadingListener;
// set listener
itemView.setOnClickListener(this);
// find views
nameTextView = (TextView) itemView.findViewById(R.id.fragment_poi_list_item_name);
Typeface typeface = Typeface.createFromAsset(itemView.getContext().getAssets(), "fonts/nexa-light.otf");
nameTextView.setTypeface(typeface);
distanceTextView = (TextView) itemView.findViewById(R.id.fragment_poi_list_item_distance);
imageView = (ImageView) itemView.findViewById(R.id.fragment_poi_list_item_image);
}
#Override
public void onClick(View view)
{
mListener.onItemClick(view, getPosition(), getItemId(), getItemViewType());
}
public void bindData(PoiModel poi, Location location)
{
nameTextView.setText(poi.getName());
mImageLoader.displayImage(poi.getImage(), imageView, mDisplayImageOptions, mImageLoadingListener);
if(location!=null)
{
LatLng myLocation = new LatLng(location.getLatitude(), location.getLongitude());
LatLng poiLocation = new LatLng(poi.getLatitude(), poi.getLongitude());
String distance = LocationUtility.getDistanceString(LocationUtility.getDistance(myLocation, poiLocation), LocationUtility.isMetricSystem());
distanceTextView.setText(distance);
distanceTextView.setVisibility(View.VISIBLE);
}
else
{
distanceTextView.setVisibility(View.GONE);
}
}
}
public static final class ImageFooterViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private PoiViewHolder.OnItemClickListener mListener;
public interface OnItemClickListener {
public void onItemClick(View view, int position, long id, int viewType);
}
public ImageFooterViewHolder(View itemView, PoiViewHolder.OnItemClickListener listener) {
super(itemView);
mListener = listener;
// set listener
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
mListener.onItemClick(view, getPosition(), getItemId(), getItemViewType());
}
public void bindData(PoiModel poi, Location location) {
}
}
public static final class FooterViewHolder extends RecyclerView.ViewHolder
{
public FooterViewHolder(View itemView)
{
super(itemView);
}
public void bindData(Object object)
{
// do nothing
}
}
}
Can't seem to find where the problem might be.
Thanks in advance!

Look at your implementation of PoiListAdapter#setItemMargins(View view, int position) There, every time the method public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) is called, inside setItemMargins
if(position<mGridSpanCount)
{
height += marginTop;
}
Take into consideration, that public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) may not be called once. The code above adds a value every time is it called.

I can suggest to use typed adapter. I've created base class for this:
public abstract class BaseRecyclerViewSpaceAdapter <D, T extends BaseRecyclerViewSpaceAdapter.FooterViewHolder> extends BaseRecyclerAdapter<D, BaseRecyclerViewSpaceAdapter.FooterViewHolder> {
public static final int ITEM = 0;
public static final int FOOTER = 1;
private int footerHeight = 240;
public int getFooterHeight() {
return footerHeight;
}
public void setFooterHeight(int footerHeight) {
this.footerHeight = footerHeight;
}
#Override
public BaseRecyclerViewSpaceAdapter.FooterViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
if(viewType == ITEM){
return onCreateItemViewHolder(parent, viewType);
}else{
View view = new View(parent.getContext());
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getFooterHeight()));
return new FooterViewHolder(view);
}
}
#Override
public void onBindViewHolder(BaseRecyclerViewSpaceAdapter.FooterViewHolder holder, int position){
if(getItemViewType(position) == ITEM){
onBindItemViewHolder((T)holder, position);
}
}
#Override
public void onBindViewHolder(BaseRecyclerViewSpaceAdapter.FooterViewHolder holder, int position, List<Object> payloads) {
if(getItemViewType(position) == ITEM && payloads != null && !payloads.isEmpty()){
onBindItemViewHolder((T)holder, position, payloads);
} else {
super.onBindViewHolder(holder, position, payloads);
}
}
#Override
public int getItemViewType(int position) {
return position == dataList.size() ? FOOTER : ITEM;
}
#Override
public int getItemCount() {
return dataList == null ? 0 : dataList.size() + 1; //added footer item
}
protected abstract void onBindItemViewHolder(T holder, int position);
protected abstract void onBindItemViewHolder(T holder, int position, List<Object> payloads);
public abstract T onCreateItemViewHolder(ViewGroup parent, int viewType);
public class FooterViewHolder extends RecyclerView.ViewHolder {
public FooterViewHolder(View view) {
super(view);
}
}
}

Related

(android) Problems with random changes in dynamic nested recycler view items

Hi,
I'm making a dynamic Nested RecyclerView.
The Outer RecyclerView (Routine) consists of parts (A, B, C...), add button,
and delete button, and the inner RecyclerView (Detail Routine) consists of text view (set, weight and count).
When i add an outer item, it have one inner RecyclerView item by default.
I thought the implementation was successful, but it wasn't.
The picture is a condition that I have made some items in advance.
This is a picture that creates additional items there.
However, in the picture, when I added the G item, you can see that the F item
and the B item have a change.
And the G item, oddly enough, has two basic items. Originally, it is one.
The second picture is the later item addition.
If i add a new outer recyclerview item, there will be a new change in the whole
item again.
I am not sure why this change is happening. If you add a new item, you want the
existing item to remain.
Please tell me the cause
Here is Code
RoutineAdapter.java ( Outer RecyclerView Adpater )
public class RoutineAdapter extends RecyclerView.Adapter<RoutineAdapter.ViewHolder> {
Context context;
ArrayList<RoutineModel> routineItems = new ArrayList<>();
public RoutineDetailAdapter detailAdapter;
OnRoutineItemClickListener listener;
public void setOnRoutineClickListener(OnRoutineItemClickListener listener) {
this.listener = listener;
}
public void addItem(RoutineModel item) {
routineItems.add(item);
notifyDataSetChanged();
}
public void addDetailItem(RoutineDetailAdapter curDetailAdapter) {
curDetailAdapter.addItem(new RoutineDetailModel());
}
public void deleteDetailItem(RoutineDetailAdapter curDetailAdapter) {
curDetailAdapter.deleteItem();
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View itemView = inflater.inflate(R.layout.routine_item, parent, false);
ViewHolder holder = new ViewHolder(itemView);
detailAdapter = new RoutineDetailAdapter();
holder.setRoutineDetailRecyClerView();
holder.rv_detail.setAdapter(detailAdapter);
detailAdapter.addItem(new RoutineDetailModel());
return holder;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
RoutineModel curRoutineItem = routineItems.get(position);
holder.setItems(curRoutineItem);
}
#Override
public int getItemCount() {
return routineItems.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
RecyclerView rv_detail;
TextView routine;
Button addSet;
Button deleteSet;
public ViewHolder(#NonNull View itemView) {
super(itemView);
initViews();
addSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
RoutineDetailAdapter curDetailAdapter = (RoutineDetailAdapter) rv_detail.getAdapter();
listener.OnAddBtnClick(curDetailAdapter);
}
});
deleteSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
RoutineDetailAdapter curDetailAdapter = (RoutineDetailAdapter) rv_detail.getAdapter();
listener.OnDeleteBtnClick(curDetailAdapter);
}
});
}
private void initViews() {
routine = itemView.findViewById(R.id.routine);
rv_detail = itemView.findViewById(R.id.detail_routine);
addSet = itemView.findViewById(R.id.add_set);
deleteSet = itemView.findViewById(R.id.delete_set);
}
private void setItems(RoutineModel routineItem) {
routine.setText(routineItem.getRoutine());
}
public void setRoutineDetailRecyClerView() {
rv_detail.setLayoutManager(new LinearLayoutManager(context, RecyclerView.VERTICAL, false));
}
}
public interface OnRoutineItemClickListener {
public void OnAddBtnClick(RoutineDetailAdapter detailAdapter);
public void OnDeleteBtnClick(RoutineDetailAdapter detailAdapter);
}
}
RoutineDetailAdapter.java ( Inner RecyclerView Adapter )
public class RoutineDetailAdapter extends RecyclerView.Adapter<RoutineDetailAdapter.ViewHolder>{
ArrayList<RoutineDetailModel> items = new ArrayList<>();
Context context;
int insertPosition;
int deletePosition;
public void addItem(RoutineDetailModel item) {
items.add(item);
notifyItemInserted(insertPosition);
}
public void deleteItem() {
try {
if(items.size() > 1) { // Leave at least one set
items.remove(deletePosition);
notifyItemRemoved(deletePosition);
deletePosition -= 1;
}
} catch (Exception e) {
// empty
}
}
public ArrayList<RoutineDetailModel> getItem() {
return this.items;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(R.layout.routine_detail_item, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
RoutineDetailModel item = items.get(position);
holder.setItem(item, position);
insertPosition = position + 1;
deletePosition = position;
}
#Override
public int getItemCount() {
return items.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView set;
public ViewHolder(#NonNull View itemView) {
super(itemView);
set = itemView.findViewById(R.id.set);
}
private void setItem(RoutineDetailModel item, int setCount) {
set.setText(setCount + 1 + "set");
}
}
}
WriteRoutineActivity.java
public class WriteRoutineActivity extends AppCompatActivity {
Button add_routine_btn;
TextView title;
RecyclerView routine_rv;
RoutineAdapter routineAdapter;
LinearLayoutManager routineLayoutManger;
ArrayList<String> titleData;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_write_routine);
initViews();
setPageTitle(getIntent());
setRoutineRecyclerview();
routineAdapter = new RoutineAdapter();
routine_rv.setAdapter(routineAdapter);
add_routine_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
WorkoutListDialogFragment routineDialog = new WorkoutListDialogFragment();
routineDialog.show(getSupportFragmentManager(), "RoutineListDialog");
}
});
routineAdapter.setOnRoutineClickListener(new RoutineAdapter.OnRoutineItemClickListener() {
#Override
public void OnAddBtnClick(RoutineDetailAdapter curDetailAdapter) {
routineAdapter.addDetailItem(curDetailAdapter);
}
#Override
public void OnDeleteBtnClick(RoutineDetailAdapter curDetailAdapter) {
routineAdapter.deleteDetailItem(curDetailAdapter);
}
});
}
private void initViews() {
title = findViewById(R.id.body_part_detail_title);
routine_rv = findViewById(R.id.routine_recyclerview);
add_routine_btn = findViewById(R.id.add_routine);
}
private void setRoutineRecyclerview() {
routineLayoutManger = new LinearLayoutManager(getApplicationContext(), RecyclerView.VERTICAL, false);
routine_rv.setLayoutManager(routineLayoutManger);
routine_rv.setHasFixedSize(true);
RecyclerView.ItemDecoration divider = new DividerItemDecorator(ContextCompat.getDrawable(getApplicationContext(), R.drawable.divider));
routine_rv.addItemDecoration(divider);
}
public void setPageTitle(Intent intent) {
if(intent != null) {
titleData = intent.getStringArrayListExtra("bodypart");
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String new_title = String.join(" / ", titleData);
title.setText(new_title);
}
else {
StringBuilder new_title = new StringBuilder();
for (int i = 0; i < titleData.size(); i++) {
if (i == titleData.size() - 1) {
new_title.append(titleData.get(i));
break;
}
new_title.append(titleData.get(i)).append(" / ");
}
title.setText(new_title);
}
}
}
public void addRoutine(String routine) {
routineAdapter.addItem(new RoutineModel(routine));
routine_rv.smoothScrollToPosition(routineAdapter.getItemCount() - 1);
}
}
ADDED
Main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".data.DailyRecordDetailActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/Theme.AppBarOverlay"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/body_part_detail_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PART"
android:textColor="#color/white"
android:textAppearance="#style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<Button
android:id="#+id/add_routine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ROUTINE ADD"
android:backgroundTint="#color/light_green"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#+id/routine_recyclerview"
app:layout_constraintRight_toRightOf="parent"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/routine_recyclerview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="#+id/add_routine"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
RoutineItem.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#color/white"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp">
<LinearLayout
android:id="#+id/linear_routine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent">
<TextView
android:id="#+id/routine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingLeft="10dp"
android:text="ROUTINE"
android:textSize="16dp"
android:textStyle="bold"
android:layout_weight="2"/>
<Button
android:id="#+id/add_set"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:text="ADD"
android:textColor="#color/black"
android:gravity="center"
android:layout_weight="1"/>
<Button
android:id="#+id/delete_set"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:text="DELETE"
android:textColor="#color/black"
android:layout_weight="1"/>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/detail_routine"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toBottomOf="#+id/linear_routine"
app:layout_constraintLeft_toLeftOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
RoutineDetail.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="wrap_content"
android:orientation="horizontal"
android:layout_marginLeft="5dp"
android:padding="10dp"
android:clipToPadding="false">
<TextView
android:id="#+id/set"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="SET"/>
<TextView
android:id="#+id/weight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0 kg"/>
<TextView
android:id="#+id/reps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0"/>
</LinearLayout>
For every RoutineModel item you recreate detailAdapter but you need new instance. Check TODO in code below:
public class RoutineAdapter extends RecyclerView.Adapter<RoutineAdapter.ViewHolder> {
Context context;
ArrayList<RoutineModel> routineItems = new ArrayList<>();
//public RoutineDetailAdapter detailAdapter; //TODO move it to local variable in method onCreateViewHolder
OnRoutineItemClickListener listener;
like this
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View itemView = inflater.inflate(R.layout.routine_item, parent, false);
ViewHolder holder = new ViewHolder(itemView);
RoutineDetailAdapter detailAdapter = new RoutineDetailAdapter(); //TODO Create local variable
detailAdapter.addItem(new RoutineDetailModel());
holder.setRoutineDetailRecyClerView();
holder.rv_detail.setAdapter(detailAdapter);
return holder;
}
But this is not good way to solve this... Its better to use one Adapter with multiple item views. I have no time now to explain, I will post some code later...
EDITED
I had same problem and and solved this on this (better) way:
Create Adapter that support multiple views
public class MultipleViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private List<Object> items; //List<Object or Generic class or abstarct base Model class parent of Routine and RoutineDetails>
private OnItemClickListener listener; //TODO new interface
..............
}
Override onCreateViewHolder like this
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if(viewType == 1){
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_item, parent, false);
return new RoutineViewHolder (itemView);
}/*else{*/
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_detail_item, parent, false);
return new RoutineDetailsViewHolder (itemView);
//}
}
Override onBindViewHolder like this and create your updateViews methods
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Object object = items.get(position); //Object or Generic class or abstarct base Model class parent of Routine and RoutineDetails>
if(object instanceof RoutineModel) {
//create method
updateRoutineViews((RoutineViewHolder) holder, (RoutineModel) object, position);
}else if(object instanceof RoutineDetailsModel) {
//create method
updateRoutineDetailsViewHolder((RoutineDetailsViewHolder) holder, (RoutineDetailModel) object);
}
}
and override getItemViewType method
#Override
public int getItemViewType(int position) {
Object object = items.get(position);
if(object instanceof RoutineModel){
return 1;
}
//else if instanceOf RoutineDetailModel return 0
return 0;
}
This is so simple and better for performance...
Also in OnItemClickListener get item by position and check instanceOf object like above. You can add new item to the clickedItemPosition + 1 with adapter.notifyDataSetChanges()
In MainActivity you have to create mixed list with Routine and Routine details
List<Object> mixedList = new ArrayList<>();
for(RoutineModel rm: routineList){
mixedList.add(rm);
if(rm has routineDetailModels){ //pseudo code
for(RoutineDetailModels rmdetilas: rm.getRoutineDetailModels()){
mixedList.add(rmdetilas);
}
}
}
adpater.swapData(mixedList); //create your method to swap or set data to adapter
EDITED
handle click
public interface OnItemClickListener{
void onClick(View view, int position);
void onLongClick(View view, int position);
}
and
adapter.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onClick(View view, int position) {
Object item = (Object) adapter.getItem(position);
if(item instanceof Routine) {
if (view.getId() == R.id.delete_id) {
//TODO delete form list, notifyItemRemoved or generate all mixeddata again and swap
} else if (view.getId() == R.id.whateveryouwant) {
//TODO doSomething()
}
}
}
#Override
public void onLongClick(View view, int position) {
}
});
The code above is just an example of how to solve the problem
For more help feel free to comment
EDITED 2
public class RoutineViewHolder extends RecyclerView.ViewHolder {
public RecyclerView rv_detail;
public TextView routine;
public Button addSet;
public Button deleteSet;
public ViewHolder(#NonNull View itemView) {
super(itemView);
//initViews(); in constructor
routine = itemView.findViewById(R.id.routine);
rv_detail = itemView.findViewById(R.id.detail_routine);
addSet = itemView.findViewById(R.id.add_set);
deleteSet = itemView.findViewById(R.id.delete_set);
}
}
update method called from onBindViewHolder
private void updateRoutineViews(RoutineViewHolder holder, RoutineModel routineItem, position){
holder.routine.setText(routineItem.getRoutine());
holder.rv_detail.setText(routineItem.getWhateverYouWant());
holder.deleteSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onItemClickListener.onClick(v, position); //or holder.getAdapterPosition()
}
});
}
COMPLETE CODE
Create complete new app to test it and understand (copy/paste this code), then integrate in your app...
There are more improvements but I don't have time, this is a quick fix
Activity
public class RoutineActivity extends AppCompatActivity {
private MultipleViewAdapter adapter;
private List<RoutineModel> routineList;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_routine);
RecyclerView contentList = findViewById(R.id.list);
contentList.setLayoutManager(new LinearLayoutManager(this));
adapter = new MultipleViewAdapter(this, new ArrayList<>());
contentList.setAdapter(adapter);
adapter.setOnItemClickListener(new MultipleViewAdapter.OnItemClickListener() {
#Override
public void onClick(View view, int position) {
Object item = (Object) adapter.getItem(position);
if(item instanceof RoutineModel) {
RoutineModel routineModel = (RoutineModel) item;
if (view.getId() == R.id.add_set) {
int weight = randomInt(99);
Toast.makeText(RoutineActivity.this, "New item with weight: " + weight + " kg", Toast.LENGTH_SHORT).show();
routineModel.addDetails(new RoutineDetailsModel(routineModel.getDetailsSize() + 1, weight));
adapter.swapData(getMixedList()); // OR add item to adapter and notify item inserted
//TODO implement SnappingLinearLayoutManager and set list.smoothScroleto... routineModel
} else if (view.getId() == R.id.delete_set) {
//TODO doSomething()
boolean deleted = routineModel.removeDetails(routineModel.getDetailsSize() - 1); // -1 !!! to delete last item
Toast.makeText(RoutineActivity.this, deleted ? "Last item is deleted": "No more items", Toast.LENGTH_SHORT).show();
adapter.swapData(getMixedList()); // OR remove item from adapter and notify item removed
//TODO implement SnappingLinearLayoutManager and set list.smoothScroleto.... routineModel
}
}else if(item instanceof RoutineDetailsModel) {
RoutineDetailsModel routineModel = (RoutineDetailsModel) item;
Toast.makeText(RoutineActivity.this, "Weight: " + routineModel.getWeight() + " kg", Toast.LENGTH_SHORT).show();
//TODO EDITED 4 (copy/paste this) random new Weight
routineModel.setWeight(randomInt(99));
adapter.notifyItemChanged(position);
Toast.makeText(RoutineActivity.this, "New Weight: " + routineModel.getWeight() + " kg", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onLongClick(View view, int position) {
}
});
initFakeData();
adapter.swapData(getMixedList());
}
private List<Object> getMixedList() {
List<Object> mixedList = new ArrayList<>();
for(RoutineModel rm: routineList){
mixedList.add(rm);
if(rm.getRoutineDetailsModel() != null && rm.getRoutineDetailsModel().size() > 0){
for(RoutineDetailsModel rmdetilas: rm.getRoutineDetailsModel()){
mixedList.add(rmdetilas);
}
}
}
return mixedList;
}
private void initFakeData() {
routineList = new ArrayList<>();
for(int i = 0; i < 5; i++){
RoutineModel routineModel = new RoutineModel(String.valueOf(i + 1));
for(int j = 0; j < 4; j++){
routineModel.addDetails(new RoutineDetailsModel(j+1, randomInt(99)));
}
routineList.add(routineModel);
}
}
private int randomInt(int max) {
return (int) Math.floor(Math.random() * max);
}
}
Activity Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
Adapter
public class MultipleViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Context context; // TODO only if you need conetxt
private List<Object> items;
private OnItemClickListener onItemClickListener;
public MultipleViewAdapter(Context context, List<Object> items) {
this.context = context;
this.items = items;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if(viewType == 1){
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_item, parent, false);
return new RoutineViewHolder(itemView);
}/*else{*/
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_detail_item, parent, false);
return new RoutineDetailsViewHolder(itemView);
//}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Object object = items.get(position); //Object or Generic class or abstarct base Model class parent of Routine and RoutineDetails>
if(object instanceof RoutineModel) {
updateRoutineViews((RoutineViewHolder) holder, (RoutineModel) object, position);
}else if(object instanceof RoutineDetailsModel) {
updateRoutineDetailsViewHolder((RoutineDetailsViewHolder) holder, (RoutineDetailsModel) object, position);
}
}
private void updateRoutineViews(RoutineViewHolder holder, RoutineModel routineItem, int position){
holder.routine.setText("Routine " + routineItem.getRoutine());
holder.addSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onItemClickListener != null) onItemClickListener.onClick(v, position);
}
});
holder.deleteSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onItemClickListener != null) onItemClickListener.onClick(v, position);
}
});
}
private void updateRoutineDetailsViewHolder(RoutineDetailsViewHolder holder, RoutineDetailsModel routineDetailsModel, int position){
holder.set.setText(routineDetailsModel.getSet() + " set");
holder.weight.setText(routineDetailsModel.getWeight() + " kg");
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onItemClickListener != null) onItemClickListener.onClick(v, position);
}
});
}
#Override
public int getItemViewType(int position) {
Object object = items.get(position);
if(object instanceof RoutineModel){
return 1;
}
//else if instanceOf RoutineDetailModel return 0
return 0;
}
#Override
public int getItemCount() {
if(items == null) return 0;
return items.size();
}
public Object getItem(int position) {
if(this.items == null || position < 0 || position >= this.items.size()) return null;
return this.items.get(position);
}
public void swapData(List<Object> newItems) {
if (newItems != null) {
this.items = newItems;
notifyDataSetChanged();
}
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public interface OnItemClickListener{
void onClick(View view, int position);
void onLongClick(View view, int position);
}
public static class RoutineViewHolder extends RecyclerView.ViewHolder {
public TextView routine;
public Button addSet;
public Button deleteSet;
public RoutineViewHolder(#NonNull View itemView) {
super(itemView);
//initViews(); in constructor
routine = itemView.findViewById(R.id.routine);
addSet = itemView.findViewById(R.id.add_set);
deleteSet = itemView.findViewById(R.id.delete_set);
}
}
public static class RoutineDetailsViewHolder extends RecyclerView.ViewHolder {
public TextView set;
public TextView weight;
public RoutineDetailsViewHolder(#NonNull View itemView) {
super(itemView);
set = itemView.findViewById(R.id.set);
weight = itemView.findViewById(R.id.weight);
}
}
}
RoutineModel
public class RoutineModel {
private List<RoutineDetailsModel> routineDetailsList;
private String routine;
public RoutineModel(String routine) {
this.routine = routine;
}
public List<RoutineDetailsModel> getRoutineDetailsModel() {
return routineDetailsList;
}
public void setRoutineDetailsModel(List<RoutineDetailsModel> routineDetailsModel) {
this.routineDetailsList = routineDetailsModel;
}
public void addDetails(RoutineDetailsModel item) {
if(routineDetailsList == null) {
routineDetailsList = new ArrayList<>();
}
this.routineDetailsList.add(item);
}
public boolean removeDetails(int index) {
if(routineDetailsList == null || index >= routineDetailsList.size() || index < 0) return false;
this.routineDetailsList.remove(index);
return true;
}
public String getRoutine() {
return routine;
}
public void setRoutine(String routine) {
this.routine = routine;
}
public int getDetailsSize() {
if(routineDetailsList == null) return 0;
return routineDetailsList.size();
}
}
RoutineDetailsModel
public class RoutineDetailsModel {
private int set;
private int weight;
public RoutineDetailsModel(int set, int weight) {
this.set = set;
this.weight = weight;
}
public int getSet() {
return set;
}
public void setSet(int set) {
this.set = set;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
Item layouts
Find in question: routine_item.xml and routine_detail_item.xml

RecyclerView can’t load an image of banner and data listitem

The RecycleView lives in a in Fragment.
it has a banner as headview and listitem.
The app can run,but can't load images and data of item.
It only has the banner of INDICATOR and its img title to run.
The logcat warns about it:
18204-18204/newsdaily.annle.com.happyreading_original2018718v0013
W/View: requestLayout() improperly called by
android.view.View{945cd0e V.ED..... ......ID 9336,141-9564,150}
during layout: running second layout pass
package newsdaily.annle.com.**.homepage;
import com.youth.banner.Banner;
import com.youth.banner.BannerConfig;
import com.youth.banner.Transformer;
import com.youth.banner.listener.OnBannerListener;
import com.youth.banner.loader.ImageLoader;
public class ChildFragment extends Fragment {
private HomePagerArticleAdapter homePagerArticleAdapter;
private RecyclerView recycleView;
private RecyclerView.LayoutManager layoutManager;
private ArrayList<HpItemArticle> articleList = new ArrayList<>();
private Banner banner;
private ArrayList<String> titles;
private ArrayList<Integer> images;
private View header;
private View view;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = LayoutInflater.from(getActivity()).inflate(R.layout.homepage_list_fragment, container, false);
Log.i("dfdfff开始", "有没有进入");
initData();
initRecycleView();
return view;
}
private void initData() {
for (int i = 0; i < 15; i++) {
HpItemArticle hpItemArticle = new HpItemArticle(
i,
"数据标题" + i,
"内容究竟是什么,我也不知道,就乱写先吧hhhhhhh" + i,
R.mipmap.ic_launcher);
articleList.add(i, hpItemArticle);
}
images = new ArrayList<>();
images.add(R.drawable.pic0);
images.add(R.drawable.pic1);
images.add(R.drawable.pic2);
titles = new ArrayList<>();
titles.clear();
for (int i = 0; i < images.size(); i++) {
titles.add("第" + i + "张图片");
}
}
private void initRecycleView() {
recycleView = (RecyclerView) view.findViewById(R.id.homepage_rcv_article);
layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
recycleView.setLayoutManager(layoutManager);
recycleView.setItemAnimator(new DefaultItemAnimator());
recycleView.setHasFixedSize(true);
initBanner();
homePagerArticleAdapter = new HomePagerArticleAdapter(getContext());
homePagerArticleAdapter.setmHeaderView(header);
homePagerArticleAdapter.addListData(articleList);
homePagerArticleAdapter.notifyDataSetChanged();
recycleView.setAdapter(homePagerArticleAdapter);
layoutManager.setItemPrefetchEnabled(false);
}
private void initBanner() {
header = LayoutInflater.from(view.getContext()).inflate(R.layout.homepage_news_header, null);
banner = (Banner) header.findViewById(R.id.banner);
//设置图片加载器
banner.setImageLoader(new FragmentImageLoader());
banner.setImages(images);
banner.setBannerTitles(titles);
banner.isAutoPlay(true);
//设置轮播时间
banner.setDelayTime(1500);
banner.start();
banner.setOnBannerListener(new OnBannerListener() {
#Override
public void OnBannerClick(int position) {
Log.i("---184--", "第" + position + "张轮播图点击了!");
}
});
}
private class FragmentImageLoader extends ImageLoader {
#Override
public void displayImage(Context context, Object path, ImageView imageView) {
Glide.with(getActivity()).load(path).into(imageView);
}
}
}
package newsdaily.annle.com.**.adapter;
/**
* 首页新闻列表适配器,头部banner,下面文章列表
* Created by 11581 on 2018/9/28.
*/
public class HomePagerArticleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private View homepageHeadView;
te ArrayList<HpItemArticle> listData = new ArrayList<>();
private LayoutInflater mLayoutInflater;
private OnItemClickListener itemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
itemClickListener = onItemClickListener;
}
public View getmHeaderView() {
return homepageHeadView;
}
public void setmHeaderView(View headerview) {
homepageHeadView = headerview;
notifyItemInserted(0);//插入下标0位置
}
public int getTypeItem(int position) {
if (homepageHeadView == null) return TYPE_ITEM;
if (position == 0) return TYPE_HEADER;
return TYPE_ITEM;
}
public HomePagerArticleAdapter(Context context) {
mLayoutInflater = LayoutInflater.from(context);
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (homepageHeadView != null && viewType == TYPE_HEADER)
return new ItemArticleViewHolder(homepageHeadView);
View view =
LayoutInflater.from(parent.getContext()).inflate(R.layout.homepage_news_item, parent, false);
return new ItemArticleViewHolder(view);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if (getItemViewType(position) == TYPE_HEADER) return;
final int pos = getPosition(holder);
final HpItemArticle data = listData.get(pos);
if (holder instanceof ItemArticleViewHolder) {
//转型为了便捷
ItemArticleViewHolder newHolder = (ItemArticleViewHolder) holder;
newHolder.hpItemArticleTitle.setText(data.getTitle());
newHolder.hpItemArticleImage.setImageResource(data.getImages());
newHolder.hpItemArticleImage.setImageResource(data.getImages());
if (itemClickListener == null) return;
((ItemArticleViewHolder) holder).itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
itemClickListener.onItemClick(position, listData.get(position).getTitle());
}
});
}
}
#Override
public int getItemCount() {
return homepageHeadView == null ? listData.size() : listData.size() + 1;
}
public int getPosition(RecyclerView.ViewHolder holder) {
int position = holder.getLayoutPosition();
return homepageHeadView == null ? position : position - 1;
}
#Override
public long getItemId(int position) {
return position;
}
public void addListData(ArrayList<HpItemArticle> datas) {
if (datas != null) {
listData.clear();
}
listData.addAll(datas);
notifyDataSetChanged();
}
public class ItemArticleViewHolder extends RecyclerView.ViewHolder {
ImageView hpItemArticleImage;
TextView hpItemArticleTitle;
TextView hpItemArticleDetails;
public ItemArticleViewHolder(View itemView) {
super(itemView);
if (itemView == homepageHeadView) return;
hpItemArticleImage = (ImageView) itemView.findViewById(R.id.hp_item_article_image);
hpItemArticleDetails = (TextView) itemView.findViewById(R.id.hp_item_article_details);
hpItemArticleTitle = (TextView) itemView.findViewById(R.id.hp_item_article_title);
ButterKnife.bind(this, itemView);
}
}
public interface OnItemClickListener {
void onItemClick(int position, String data);
}
}
My English is not good.Thanks in advance.
banner.xml. header of RecyclerView
<?xml version="1.0" encoding="utf-8"?>
<com.youth.banner.Banner
android:id="#+id/banner"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
xmlns:android="http://schemas.android.com/apk/res/android" />
RecyclerView item layout.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/hp_item_article_image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
app:srcCompat="#mipmap/ic_launcher_round" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/hp_item_article_image"
android:orientation="vertical">
<TextView
android:id="#+id/hp_item_article_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:text="#string/homepage_article_title"
tools:ignore="HardcodedText" />
<TextView
android:id="#+id/hp_item_article_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:text="#string/homepage_article_details" />
</LinearLayout>
</RelativeLayout>

RecyclerView.Adapter shows 2 item in a single row (how to change that)

So I am working on a project which already has some code in it. I would like to change the way my adapter items are being shown.
Right now there are 2 items in each row. What I want to do is have only 1 item in each row.
Here is an image of how the image layout is right now.
2 items appear in each row while I need only 1 to appear.
Here is the code on the xmls
Item xml
<com.inthessaloniki.cityguide.view.SelectorRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment_poi_list_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:listSelector="#drawable/selector_clickable_item_bg_inverse">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="#+id/fragment_poi_list_item_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitXY"/>
</RelativeLayout>
List xml
<FrameLayout
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="match_parent"
android:animateLayoutChanges="true">
<LinearLayout
android:id="#+id/container_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/fragment_poi_list_recycler"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbars="vertical"
android:background="#color/global_bg_front" />
<com.google.android.gms.ads.AdView
android:id="#+id/fragment_poi_list_adview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:adUnitId="#string/admob_unit_id_poi_list"
app:adSize="BANNER" />
</LinearLayout>
<include
layout="#layout/placeholder_progress"
android:id="#+id/container_progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<include
layout="#layout/placeholder_offline"
android:id="#+id/container_offline"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<include
layout="#layout/placeholder_empty"
android:id="#+id/container_empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
ListAdapter code
public class SubCategoryListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
{
public static final int VIEW_TYPE_POI = 1;
public static final int VIEW_TYPE_IMAGE_FOOTER = 2;
public static final int VIEW_TYPE_FOOTER = 3;
private List<CategoryModel> mPoiList;
private List<Object> mFooterList;
private PoiViewHolder.OnItemClickListener mListener;
private int mGridSpanCount;
private Location mLocation;
private boolean mAnimationEnabled = true;
private int mAnimationPosition = -1;
private ImageLoader mImageLoader = ImageLoader.getInstance();
private DisplayImageOptions mDisplayImageOptions;
private ImageLoadingListener mImageLoadingListener;
//Ares
private long mCategory;
public SubCategoryListAdapter(List<CategoryModel> poiList, List<Object> footerList, PoiViewHolder.OnItemClickListener listener, int gridSpanCount, Location location, long category)
{
mPoiList = poiList;
mFooterList = footerList;
mListener = listener;
mGridSpanCount = gridSpanCount;
mLocation = location;
//Ares
mCategory = category;
// image caching options
mDisplayImageOptions = new DisplayImageOptions.Builder()
.showImageOnLoading(android.R.color.transparent)
.showImageForEmptyUri(R.drawable.placeholder_photo)
.showImageOnFail(R.drawable.placeholder_photo)
.cacheInMemory(true)
.cacheOnDisk(true)
.displayer(new SimpleBitmapDisplayer())
.build();
mImageLoadingListener = new AnimateImageLoadingListener();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
// inflate view and create view holder
View view;
if(viewType== VIEW_TYPE_POI)
{
if(mCategory == -4) {
view = inflater.inflate(R.layout.fragment_poi_list_item_home, parent, false);
}
else
{
view = inflater.inflate(R.layout.fragment_poi_list_item, parent, false);
}
return new PoiViewHolder(view, mListener, mImageLoader, mDisplayImageOptions, mImageLoadingListener, mCategory);
}
else if(viewType==VIEW_TYPE_IMAGE_FOOTER)
{
view = inflater.inflate(R.layout.footer_image_layout, parent, false);
return new ImageFooterViewHolder(view, mListener);
}
else if(viewType==VIEW_TYPE_FOOTER)
{
view = inflater.inflate(R.layout.fragment_poi_list_footer, parent, false);
return new FooterViewHolder(view);
}
else
{
throw new RuntimeException("There is no view type that matches the type " + viewType);
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position)
{
// bind data
if(viewHolder instanceof PoiViewHolder)
{
// entity
CategoryModel poi = mPoiList.get(getPoiPosition(position));
// render view
if(poi != null)
{
((PoiViewHolder) viewHolder).bindData(poi, mLocation, mCategory);
}
}
else if(viewHolder instanceof FooterViewHolder)
{
// entity
Object object = mFooterList.get(getFooterPosition(position));
// render view
if(object != null)
{
((FooterViewHolder) viewHolder).bindData(object);
}
}
// set item margins
setItemMargins(viewHolder.itemView, position);
// set animation
setAnimation(viewHolder.itemView, position);
}
#Override
public int getItemCount()
{
int size = 0;
if(mPoiList !=null) size += mPoiList.size();
if(mFooterList!=null) size += mFooterList.size();
if(mFooterList!=null) size += 1; //ImageFooter
return size;
}
#Override
public int getItemViewType(int position)
{
int pois = mPoiList.size();
int footers = mFooterList.size();
int imageFooter = 1;
if(position < pois) return VIEW_TYPE_POI;
else if(position < pois+imageFooter) return VIEW_TYPE_IMAGE_FOOTER;
else if(position < pois+imageFooter+footers) return VIEW_TYPE_FOOTER;
else return -1;
}
public int getPoiCount()
{
if(mPoiList !=null) return mPoiList.size();
return 0;
}
public int getFooterCount()
{
if(mFooterList!=null) return mFooterList.size();
return 0;
}
public int getPoiPosition(int recyclerPosition)
{
return recyclerPosition;
}
public int getFooterPosition(int recyclerPosition)
{
return recyclerPosition - getPoiCount();
}
public int getRecyclerPositionByPoi(int poiPosition)
{
return poiPosition;
}
public int getRecyclerPositionByFooter(int footerPosition)
{
return footerPosition + getPoiCount();
}
public void refill(List<CategoryModel> poiList, List<Object> footerList, PoiViewHolder.OnItemClickListener listener, int gridSpanCount, Location location)
{
mPoiList = poiList;
mFooterList = footerList;
mListener = listener;
mGridSpanCount = gridSpanCount;
mLocation = location;
notifyDataSetChanged();
}
public void stop()
{
}
public void setLocation(Location location)
{
mLocation = location;
}
public void setAnimationEnabled(boolean animationEnabled)
{
mAnimationEnabled = animationEnabled;
}
private void setAnimation(final View view, int position)
{
if(mAnimationEnabled && position>mAnimationPosition)
{
view.setScaleX(0f);
view.setScaleY(0f);
view.animate()
.scaleX(1f)
.scaleY(1f)
.setDuration(300)
.setInterpolator(new DecelerateInterpolator());
mAnimationPosition = position;
}
}
private void setItemMargins(View view, int position)
{
int height = 0;
if(mCategory != -4)
height = (int) CityGuideApplication.getContext().getResources().getDimension(R.dimen.fragment_poi_list_recycler_item_size);
int marginTop = 0;
if(position<mGridSpanCount)
{
TypedArray a = CityGuideApplication.getContext().obtainStyledAttributes(null, new int[]{android.R.attr.actionBarSize}, 0, 0);
marginTop = (int) a.getDimension(0, 0);
a.recycle();
if(mCategory != -4)
height += marginTop;
}
ViewGroup.MarginLayoutParams marginParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
marginParams.setMargins(0, marginTop, 0, 0);
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
if(mCategory != -4)
layoutParams.height = height;
}
public static final class PoiViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
{
private TextView nameTextView;
private TextView distanceTextView;
private ImageView imageView;
private OnItemClickListener mListener;
private ImageLoader mImageLoader;
private DisplayImageOptions mDisplayImageOptions;
private ImageLoadingListener mImageLoadingListener;
public interface OnItemClickListener
{
public void onItemClick(View view, int position, long id, int viewType);
}
public PoiViewHolder(View itemView, OnItemClickListener listener, ImageLoader imageLoader, DisplayImageOptions displayImageOptions, ImageLoadingListener imageLoadingListener, long category)
{
super(itemView);
mListener = listener;
mImageLoader = imageLoader;
mDisplayImageOptions = displayImageOptions;
mImageLoadingListener = imageLoadingListener;
// set listener
itemView.setOnClickListener(this);
// find views
if(category == -4)
{
Log.d("Category Status: ", String.valueOf(category));
}
else
{
nameTextView = (TextView) itemView.findViewById(R.id.fragment_poi_list_item_name);
distanceTextView = (TextView) itemView.findViewById(R.id.fragment_poi_list_item_distance);
distanceTextView.setVisibility(View.GONE);
}
imageView = (ImageView) itemView.findViewById(R.id.fragment_poi_list_item_image);
}
#Override
public void onClick(View view)
{
mListener.onItemClick(view, getPosition(), getItemId(), getItemViewType());
}
public void bindData(CategoryModel subCategory, Location location, long category)
{
if(category == -4)
{
}
else
{
nameTextView.setText(subCategory.getName());
}
mImageLoader.displayImage(subCategory.getImage2(), imageView, mDisplayImageOptions, mImageLoadingListener);
// if(location!=null)
// {
// LatLng myLocation = new LatLng(location.getLatitude(), location.getLongitude());
// LatLng poiLocation = new LatLng(poi.getLatitude(), poi.getLongitude());
// String distance = LocationUtility.getDistanceString(LocationUtility.getDistance(myLocation, poiLocation), LocationUtility.isMetricSystem());
// distanceTextView.setText(distance);
// distanceTextView.setVisibility(View.VISIBLE);
// }
// else
// {
// distanceTextView.setVisibility(View.GONE);
// }
}
}
public static final class ImageFooterViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private PoiViewHolder.OnItemClickListener mListener;
public interface OnItemClickListener {
public void onItemClick(View view, int position, long id, int viewType);
}
public ImageFooterViewHolder(View itemView, PoiViewHolder.OnItemClickListener listener) {
super(itemView);
mListener = listener;
// set listener
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
mListener.onItemClick(view, getPosition(), getItemId(), getItemViewType());
}
public void bindData(PoiModel poi, Location location) {
}
}
public static final class FooterViewHolder extends RecyclerView.ViewHolder
{
public FooterViewHolder(View itemView)
{
super(itemView);
}
public void bindData(Object object)
{
// do nothing
}
}
}
GritLayout
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration
{
private int mSpacing;
public GridSpacingItemDecoration(int spacingPixelSize)
{
mSpacing = spacingPixelSize;
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView recyclerView, RecyclerView.State state)
{
super.getItemOffsets(outRect, view, recyclerView, state);
int position = recyclerView.getChildPosition(view);
int spanCount = getSpanCount(recyclerView);
int itemCount = recyclerView.getAdapter().getItemCount();
// top offset
if(position < spanCount)
{
outRect.top = mSpacing;
}
else
{
outRect.top = mSpacing/2;
}
// bottom offset
if(itemCount%spanCount == 0 && position >= itemCount - spanCount)
{
outRect.bottom = mSpacing;
}
else if(itemCount%spanCount != 0 && position >= itemCount - itemCount%spanCount)
{
outRect.bottom = mSpacing;
}
else
{
outRect.bottom = mSpacing/2;
}
// left offset
if(position%spanCount == 0)
{
outRect.left = mSpacing;
}
else
{
outRect.left = mSpacing/2;
}
// right offset
if(position%spanCount == spanCount-1)
{
outRect.right = mSpacing;
}
else
{
outRect.right = mSpacing/2;
}
}
private int getSpanCount(RecyclerView recyclerView)
{
if(recyclerView.getLayoutManager() instanceof GridLayoutManager)
{
GridLayoutManager gridLayoutManager = (GridLayoutManager) recyclerView.getLayoutManager();
return gridLayoutManager.getSpanCount();
}
else
{
throw new IllegalStateException(this.getClass().getSimpleName() + " can only be used with a " + GridLayoutManager.class.getSimpleName());
}
}
}
I don't think that the problem is in the XML files. It must be somewhere in the code. I just can't track it down and modify it... :S
I've been searching for hours.
Thanks in advance!!!
You have not posted the piece of code where you are setting LayoutManager to your RecyclerView.
Is seems you are using GridLayoutManager:
RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(context, 2);
myRecyclerView.setLayoutManager(mLayoutManager);
Try changing it to LinearLayoutManager:
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(context);
myRecyclerView.setLayoutManager(mLayoutManager);

how to display multiply layout in RecyclerView [duplicate]

This question already has answers here:
How to create RecyclerView with multiple view types
(23 answers)
Closed 6 years ago.
I am getting data in jSON. Data contains images url. I am trying to display images in below layouts.
1Image
2Image
3Image 4Image 5Image 6Image 7image
8Image 9Image 10Image 11Image 12image
13image 14image 15image 16image 17image
.... .... ..... ...... .....
So First two image will be in vertically. Other 5 images in horizontal scroll view and so on.
So i am using RecyclerView with vertical orientation. but i am not able to creating the logic so images will be display above format.
please anyone guide me.
update:-
//Horizontal images view.
public class MoviesAdapterHorizontal extends RecyclerView.Adapter<MoviesAdapterHorizontal.MyViewHolder> {
private List<String> moviesList;
// private ThumbnailListener thumbnailListener;
public class MyViewHolder extends RecyclerView.ViewHolder {
// YouTubeThumbnailView thumbnail;
ImageView imageView2;
public MyViewHolder(View view) {
super(view);
// thumbnail = (YouTubeThumbnailView) view.findViewById(R.id.thumbnail);
imageView2 = (ImageView) view.findViewById(R.id.imageView2);
}
}
public MoviesAdapterHorizontal(List<String> moviesList) {
this.moviesList = moviesList;
// thumbnailListener = new ThumbnailListener();
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.hotrow2, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final String url = moviesList.get(position);
System.out.println("movie url = " + url);
final String m = url.substring(url.lastIndexOf('/') + 1);
System.out.println("url = "+m);
int w = BasicDeviceInfo.getWidth(getActivity());
// int h = BasicDeviceInfo.getHeight(getActivity());
LinearLayout.LayoutParams lParams =
new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
w/3);
AQuery aq = new AQuery(holder.imageView2);
aq.id(holder.imageView2).image("http://x.x.x.231/news-flicks/uploads/home/tulips_e84aa48cee1a2a25aef001cd74152663.jpg", true, true, 0,
R.drawable.no_thumbnail);
holder.imageView2.setLayoutParams(lParams);
// holder.thumbnail.setLayoutParams(lParams);
/* holder.thumbnail.setTag(m);
holder.thumbnail.initialize("AIzaSyDW-sxPUqy2rD6ZWs3vTNb0jKEKA21RjrY", thumbnailListener);
holder.thumbnail.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), FullscreenDemoActivity.class);
intent.putExtra("url",m);
intent.putExtra("txt", "");
startActivity(intent);
}
});*/
}
#Override
public int getItemCount() {
return moviesList.size();
}
private final class ThumbnailListener implements
YouTubeThumbnailView.OnInitializedListener,
YouTubeThumbnailLoader.OnThumbnailLoadedListener {
#Override
public void onInitializationSuccess(
YouTubeThumbnailView view, YouTubeThumbnailLoader loader) {
loader.setOnThumbnailLoadedListener(this);
String videoId = (String) view.getTag();
loader.setVideo(videoId);
}
#Override
public void onInitializationFailure(
YouTubeThumbnailView view, YouTubeInitializationResult loader) {
view.setImageResource(R.drawable.no_thumbnail);
}
#Override
public void onThumbnailLoaded(YouTubeThumbnailView view, String videoId) {
}
#Override
public void onThumbnailError(YouTubeThumbnailView view, YouTubeThumbnailLoader.ErrorReason errorReason) {
view.setImageResource(R.drawable.no_thumbnail);
}
}
}
horizontal 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="wrap_content"
android:orientation="vertical"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/imageView2"
android:src="#drawable/no_thumbnail"/>
</LinearLayout>
i am calling it parent recycle view onBindViewHolder()
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
Videos movie = moviesList.get(position);
System.out.println("Image = " + movie.getImage());
int w = BasicDeviceInfo.getWidth(getActivity());
// int h = BasicDeviceInfo.getHeight(getActivity());
if(position < 2) {
final List<String> lst = new ArrayList<String>();
lst.add(movie.getVideo_url());
lst.add(movie.getVideo_url());
lst.add(movie.getVideo_url());
lst.add(movie.getVideo_url());
lst.add(movie.getVideo_url());
final MoviesAdapterHorizontal adapter = new MoviesAdapterHorizontal(lst);
holder.recyclerView2.setAdapter(adapter);
adapter.notifyDataSetChanged();
Now my horizontal onBindViewHolder is calling and able to see log messages. But ImageView is not displaying. i think it is hiding or overlapping
See onto this Answer for how to make different layout item in RecyclerView
For first two element layout file containing ImageView
For rest item view I will suggest take Horizontal RecyclerView as item
My Custom view for Horizontal RecyclerView
public class DocumentViewDynamic extends LinearLayout {
private static final String LOG_TAG = DocumentViewDynamic.class.getSimpleName();
private final String titleStr;
private OnDocumentItemClicked callBack;
public void updateImage(Bitmap bitmap, int postionOfView) {
Log.e(LOG_TAG, "updateImage: ");
documentAdapter.setImageFromCamera(bitmap, postionOfView);
}
public interface OnDocumentItemClicked {
void onDocumentItemClicked(View v, int position);
}
public void setItemClickedListener(OnDocumentItemClicked callBack) {
this.callBack = callBack;
}
#Bind(R.id.title)
TextView title;
#Bind(R.id.img_add)
FloatingActionButton imgAdd;
private DocumentAdapter documentAdapter;
private final Context context;
#Bind(R.id.rv_document)
RecyclerView rvDocument;
#OnClick(R.id.img_add)
public void addDummyImage() {
documentAdapter.addSingelDummyImage();
rvDocument.scrollToPosition(documentAdapter.getItemCount() - 1);
}
public DocumentViewDynamic(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.DocumentViewDynamic, 0, 0);
try {
titleStr = ta.getString(R.styleable.DocumentViewDynamic_titleDoc);
} finally {
ta.recycle();
}
init();
}
public ArrayList<DocumentModel> getAllImageData() {
return documentAdapter.getImageData();
}
private void init() {
setOrientation(VERTICAL);
View view = LayoutInflater.from(context).inflate(R.layout.compund_view_document, this);
ButterKnife.bind(this, view);
if (!TextUtils.isEmpty(titleStr))
title.setText(titleStr);
initRecyclerView();
}
private void initRecyclerView() {
rvDocument.setLayoutManager(new LinearLayoutManager(context, HORIZONTAL, false));
ArrayList<DocumentModel> documentModels = new ArrayList<>();
documentAdapter = new DocumentAdapter(documentModels);
rvDocument.setAdapter(documentAdapter);
}
public class DocumentAdapter extends RecyclerView.Adapter<DocumentAdapter.DocViewHolder> {
public ArrayList<DocumentModel> getImageData() {
return documentModels;
}
private ArrayList<DocumentModel> documentModels = new ArrayList<>();
public DocumentAdapter(ArrayList<DocumentModel> documentModels) {
this.documentModels = documentModels;
if (documentModels.size() == 0)
documentModels.add(new DocumentModel());
}
#Override
public DocViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_compund_document, parent, false);
return new DocViewHolder(view);
}
#Override
public void onBindViewHolder(final DocViewHolder holder, int position) {
if (documentModels.get(position).getBitmap() == null) {
holder.imgDocument.setImageResource(R.drawable.camera);
holder.imgBtnDelete.setVisibility(GONE);
} else {
holder.imgDocument.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
holder.imgDocument.setScaleType(ImageView.ScaleType.FIT_XY);
holder.imgDocument.setImageBitmap(documentModels.get(position).getBitmap());
holder.imgBtnDelete.setVisibility(VISIBLE);
}
holder.itemView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
callBack.onDocumentItemClicked(DocumentViewDynamic.this, holder.getAdapterPosition());
}
});
holder.imgBtnDelete.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (holder.getAdapterPosition() == 0 && documentModels.size() == 1) {
documentModels.get(holder.getAdapterPosition()).setBitmap(null);
} else {
documentModels.remove(holder.getAdapterPosition());
}
notifyDataSetChanged();
}
});
}
#Override
public int getItemCount() {
return (documentModels != null && documentModels.size() > 0) ? documentModels.size() : 0;
}
public void setImageFromCamera(Bitmap imageFromCamera, int postionOfView) {
documentModels.get(postionOfView).setBitmap(imageFromCamera);
notifyDataSetChanged();
}
public class DocViewHolder extends RecyclerView.ViewHolder {
#Bind(R.id.img_document)
ImageView imgDocument;
#Bind(R.id.img_btn_delete)
ImageView imgBtnDelete;
public DocViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
public void addSingelDummyImage() {
if (documentModels.get(documentModels.size() - 1).getBitmap() != null) {
documentModels.add(new DocumentModel());
notifyDataSetChanged();
} else {
Snackbar.make(rvDocument, "Please Add image ", Snackbar.LENGTH_LONG).show();
}
}
}
}
layout file for compound view
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title"
android:textColor="#3399ff"
android:textSize="15dp"
android:textStyle="normal" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_document"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="#+id/img_add" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/img_add"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:src="#drawable/fab_plus_bg" />
</RelativeLayout>
Inflate this compound view as item in Vertical RecyclerView

Creating an expandable RecyclerView

I'm trying to implement a recyclerview that behaves like my sketch below:
The idea is that there is a parent list, when an list item in the parent list is tapped, that list item reveals a child list that contains it's own data. when a list item is tapped in the child list, the value of that child is reflected and updates the value of the parent in the parent list item.
I've tried to get it working for the past 3 days to no avail. I tried using the AdvancedReyclerview library but to a beginner like me, it was a giant mess of things that didn't make sense especially when passing in the data. I copied and pasted files that I needed to get a minimal working version but I had no idea how to pass my data into the recyclerview and how to update it with the newly selected value.
Is it even possible to do what I'm trying to do or am I way out of my depth here?
If it's still difficult to understand, I can explain it more.
EDIT: someone recommended that I do this with the ExpandableListView rather than the RecyclerView. Any thoughts on that?
1.ExpandableRecyclerAdapter.class
public abstract class ExpandableRecyclerAdapter<T extends ExpandableRecyclerAdapter.ListItem> extends RecyclerView.Adapter<ExpandableRecyclerAdapter.ViewHolder> {
protected Context mContext;
protected List<T> allItems = new ArrayList<>();
protected List<T> visibleItems = new ArrayList<>();
private List<Integer> indexList = new ArrayList<>();
private SparseIntArray expandMap = new SparseIntArray();
private int mode;
protected static final int TYPE_HEADER = 1000;
private static final int ARROW_ROTATION_DURATION = 150;
public static final int MODE_NORMAL = 0;
public static final int MODE_ACCORDION = 1;
public ExpandableRecyclerAdapter(Context context) {
mContext = context;
}
public static class ListItem {
public int ItemType;
public ListItem(int itemType) {
ItemType = itemType;
}
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public int getItemCount() {
return visibleItems == null ? 0 : visibleItems.size();
}
protected View inflate(int resourceID, ViewGroup viewGroup) {
return LayoutInflater.from(mContext).inflate(resourceID, viewGroup, false);
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View view) {
super(view);
}
}
public class HeaderViewHolder extends ViewHolder {
ImageView arrow;
public HeaderViewHolder(View view) {
super(view);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
toggleExpandedItems(getLayoutPosition(),false);
/*if(isExpanded(getLayoutPosition())){
collapseItems(getLayoutPosition(),false);
}else {
expandItems(getLayoutPosition(),true);
}*/
}
});
}
public HeaderViewHolder(View view, final ImageView arrow) {
super(view);
this.arrow = arrow;
arrow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
handleClick();
}
});
}
protected void handleClick() {
if (toggleExpandedItems(getLayoutPosition(), false)) {
openArrow(arrow);
} else {
closeArrow(arrow);
}
}
public void bind(int position) {
arrow.setRotation(isExpanded(position) ? 90 : 0);
}
}
public boolean toggleExpandedItems(int position, boolean notify) {
if (isExpanded(position)) {
collapseItems(position, notify);
return false;
} else {
expandItems(position, notify);
if (mode == MODE_ACCORDION) {
collapseAllExcept(position);
}
return true;
}
}
public void expandItems(int position, boolean notify) {
int count = 0;
int index = indexList.get(position);
int insert = position;
for (int i=index+1; i<allItems.size() && allItems.get(i).ItemType != TYPE_HEADER; i++) {
insert++;
count++;
visibleItems.add(insert, allItems.get(i));
indexList.add(insert, i);
}
notifyItemRangeInserted(position + 1, count);
int allItemsPosition = indexList.get(position);
expandMap.put(allItemsPosition, 1);
if (notify) {
notifyItemChanged(position);
}
}
public void collapseItems(int position, boolean notify) {
int count = 0;
int index = indexList.get(position);
for (int i=index+1; i<allItems.size() && allItems.get(i).ItemType != TYPE_HEADER; i++) {
count++;
visibleItems.remove(position + 1);
indexList.remove(position + 1);
}
notifyItemRangeRemoved(position + 1, count);
int allItemsPosition = indexList.get(position);
expandMap.delete(allItemsPosition);
if (notify) {
notifyItemChanged(position);
}
}
protected boolean isExpanded(int position) {
int allItemsPosition = indexList.get(position);
return expandMap.get(allItemsPosition, -1) >= 0;
}
#Override
public int getItemViewType(int position) {
return visibleItems.get(position).ItemType;
}
public void setItems(List<T> items) {
allItems = items;
List<T> visibleItems = new ArrayList<>();
expandMap.clear();
indexList.clear();
for (int i=0; i<items.size(); i++) {
if (items.get(i).ItemType == TYPE_HEADER) {
indexList.add(i);
visibleItems.add(items.get(i));
}
}
this.visibleItems = visibleItems;
notifyDataSetChanged();
}
protected void removeItemAt(int visiblePosition) {
int allItemsPosition = indexList.get(visiblePosition);
allItems.remove(allItemsPosition);
visibleItems.remove(visiblePosition);
incrementIndexList(allItemsPosition, visiblePosition, -1);
incrementExpandMapAfter(allItemsPosition, -1);
notifyItemRemoved(visiblePosition);
}
private void incrementExpandMapAfter(int position, int direction) {
SparseIntArray newExpandMap = new SparseIntArray();
for (int i=0; i<expandMap.size(); i++) {
int index = expandMap.keyAt(i);
newExpandMap.put(index < position ? index : index + direction, 1);
}
expandMap = newExpandMap;
}
private void incrementIndexList(int allItemsPosition, int visiblePosition, int direction) {
List<Integer> newIndexList = new ArrayList<>();
for (int i=0; i<indexList.size(); i++) {
if (i == visiblePosition) {
if (direction > 0) {
newIndexList.add(allItemsPosition);
}
}
int val = indexList.get(i);
newIndexList.add(val < allItemsPosition ? val : val + direction);
}
indexList = newIndexList;
}
public void collapseAll() {
collapseAllExcept(-1);
}
public void collapseAllExcept(int position) {
for (int i=visibleItems.size()-1; i>=0; i--) {
if (i != position && getItemViewType(i) == TYPE_HEADER) {
if (isExpanded(i)) {
collapseItems(i, true);
}
}
}
}
public void expandAll() {
for (int i=visibleItems.size()-1; i>=0; i--) {
if (getItemViewType(i) == TYPE_HEADER) {
if (!isExpanded(i)) {
expandItems(i, true);
}
}
}
}
public static void openArrow(View view) {
view.animate().setDuration(ARROW_ROTATION_DURATION).rotation(180);
}
public static void closeArrow(View view) {
view.animate().setDuration(ARROW_ROTATION_DURATION).rotation(0);
}
public int getMode() {
return mode;
}
public void setMode(int mode) {
this.mode = mode;
}
}
2.activity_main
<?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.support.v7.widget.RecyclerView android:id="#+id/main_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
3.item_header
<?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="wrap_content"
android:orientation="horizontal"
android:padding="#dimen/standard_padding">
<LinearLayout
android:id="#+id/lnr_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true">
<TextView
android:id="#+id/txt_header_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="#mipmap/ic_usa"
android:gravity="center"
android:text="Beverly Hills"
android:textStyle="bold" />
</LinearLayout>
<ImageView
android:id="#+id/img_arrow"
android:layout_width="#dimen/arrow_size"
android:layout_height="#dimen/arrow_size"
android:layout_alignParentRight="true"
android:src="#mipmap/arrow" />
<TextView
android:id="#+id/txt_header_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:text="Home"
android:textStyle="bold" />
4.item_content.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/rcl_header_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
style="?android:attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/btn_cancle" />
<Button
style="?android:attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="#string/btn_save" />
</RelativeLayout>
<LinearLayout
android:id="#+id/lnr_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/rcl_header_btn"
android:gravity="center_vertical"
android:orientation="vertical">
<EditText
android:id="#+id/edt_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="DESCRIPTION" />
<EditText
android:id="#+id/edt_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Address" />
<LinearLayout
android:id="#+id/lnr_child_1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="#+id/edt_city"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="City" />
<EditText
android:id="#+id/edt_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="State" />
</LinearLayout>
<LinearLayout
android:id="#+id/lnr_child_2"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="#+id/edt_zipcode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Zip Code" />
<EditText
android:id="#+id/edt_country"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Country" />
</LinearLayout>
</LinearLayout>
<RelativeLayout
android:id="#+id/rcl_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/lnr_parent">
<CheckBox
android:id="#+id/chk_marked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/txt_type" />
<TextView
android:id="#+id/txt_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/btn_delete"
android:layout_toRightOf="#+id/chk_marked"
android:gravity="center"
android:text="SET AS DEFAULT" />
<Button
android:id="#+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="DELETE" />
</RelativeLayout>
5.Adapter
public class PeopleAdapter extends ExpandableRecyclerAdapter<PeopleAdapter.PeopleListItem> {
public static final int TYPE_PERSON = 1001;
public PeopleAdapter(Context context) {
super(context);
setItems(getSampleItems());
}
public static class PeopleListItem extends ExpandableRecyclerAdapter.ListItem {
public String Text;
public PeopleListItem(String group) {
super(TYPE_HEADER);
Text = group;
}
public PeopleListItem(String first, String last) {
super(TYPE_PERSON);
Text = first + " " + last;
}
}
public class HeaderViewHolder extends ExpandableRecyclerAdapter.HeaderViewHolder {
TextView name;
public HeaderViewHolder(View view) {
super(view, (ImageView) view.findViewById(R.id.img_arrow));
name = (TextView) view.findViewById(R.id.txt_header_name);
}
public void bind(int position) {
super.bind(position);
name.setText(visibleItems.get(position).Text);
}
}
public class PersonViewHolder extends ExpandableRecyclerAdapter.ViewHolder {
EditText name;
public PersonViewHolder(View view) {
super(view);
name = (EditText) view.findViewById(R.id.edt_description);
}
public void bind(int position) {
name.setText(name.getText());
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_HEADER:
return new HeaderViewHolder(inflate(R.layout.item_header, parent));
case TYPE_PERSON:
default:
return new PersonViewHolder(inflate(R.layout.item_content, parent));
}
}
#Override
public void onBindViewHolder(ExpandableRecyclerAdapter.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case TYPE_HEADER:
((HeaderViewHolder) holder).bind(position);
break;
case TYPE_PERSON:
default:
((PersonViewHolder) holder).bind(position);
break;
}
}
private List<PeopleListItem> getSampleItems() {
List<PeopleListItem> items = new ArrayList<>();
items.add(new PeopleListItem("Friends"));
items.add(new PeopleListItem("", ""));
items.add(new PeopleListItem("Friends"));
items.add(new PeopleListItem("", ""));
return items;
}
}
6.MainActivity.java
public class MainActivity extends AppCompatActivity {
RecyclerView recycler;
PeopleAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
recycler = (RecyclerView) findViewById(R.id.main_recycler);
adapter = new PeopleAdapter(this);
adapter.setMode(ExpandableRecyclerAdapter.MODE_ACCORDION);
recycler.setLayoutManager(new LinearLayoutManager(this));
recycler.setAdapter(adapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_expand_all:
adapter.expandAll();
return true;
case R.id.action_collapse_all:
adapter.collapseAll();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
You can check my library in here
And create something like below code:
public class PurchaseItemRecyclerViewAdapter extends ExpandableRecyclerView.Adapter<PurchaseItemRecyclerViewAdapter.ChildViewHolder,ExpandableRecyclerView.SimpleGroupViewHolder,String,String>
{
List<ItemModel> itemModels;
public PurchaseItemRecyclerViewAdapter() {
this.itemModels = new ArrayList<>();
itemModels.add(new ItemModel("group 0",3,"subitem :"));
itemModels.add(new ItemModel("group 1",3,"subitem :"));
itemModels.add(new ItemModel("group 2",2,"subitem :"));
itemModels.add(new ItemModel("group 3",1,"subitem :"));
itemModels.add(new ItemModel("group 4",3,"subitem :"));
itemModels.add(new ItemModel("group 5",5,"subitem :"));
}
#Override
public int getGroupItemCount() {
return itemModels.size();
}
#Override
public int getChildItemCount(int i) {
return itemModels.get(i).getSubItemCount();
}
#Override
public String getGroupItem(int i) {
return itemModels.get(i).getParentName();
}
#Override
public String getChildItem(int group, int child) {
return itemModels.get(group).getSubItemPrefix() + child;
}
#Override
protected ExpandableRecyclerView.SimpleGroupViewHolder onCreateGroupViewHolder(ViewGroup parent)
{
return new ExpandableRecyclerView.SimpleGroupViewHolder(parent.getContext());
}
#Override
protected ChildViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType)
{
View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.purchase_list_content,parent,false);
return new ChildViewHolder(rootView);
}
#Override
public void onBindGroupViewHolder(ExpandableRecyclerView.SimpleGroupViewHolder holder, int group) {
super.onBindGroupViewHolder(holder, group);
holder.setText(getGroupItem(group));
}
#Override
public void onBindChildViewHolder(ChildViewHolder holder, final int group, int position)
{
super.onBindChildViewHolder(holder, group, position);
holder.name.setText(getChildItem(group, position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
itemModels.get(group).setParentName("edited Parent");
notifyItemChanged(group);
}
});
}
#Override
public int getChildItemViewType(int i, int i1) {
return 1;
}
public class ChildViewHolder extends RecyclerView.ViewHolder
{
private TextView name;
public ChildViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.item_name);
}
}
}
and this ItemModel class:
public class ItemModel {
String parentName;
int subItemCount;
String subItemPrefix;
public ItemModel(String parentName, int subItemCount, String subItemPrefix) {
this.parentName = parentName;
this.subItemCount = subItemCount;
this.subItemPrefix = subItemPrefix;
}
public String getParentName() {
return parentName;
}
public void setParentName(String parentName) {
this.parentName = parentName;
}
public int getSubItemCount() {
return subItemCount;
}
public void setSubItemCount(int subItemCount) {
this.subItemCount = subItemCount;
}
public String getSubItemPrefix() {
return subItemPrefix;
}
public void setSubItemPrefix(String subItemPrefix) {
this.subItemPrefix = subItemPrefix;
}
}
This is bit late but take a look on this advanced recyclerview library https://github.com/h6ah4i/android-advancedrecyclerview
In their documentation you can see Expandable item related classes/interfaces
check it out.
You can easily achieve it with this library, there is a full example here.
Basically you group your items into sections:
class MySection extends StatelessSection {
String header;
List<String> list;
boolean expanded = true;
public MySection(String header, List<String> list) {
// call constructor with layout resources for this Section header and items
super(R.layout.section_header, R.layout.section_item);
this.myHeader = header;
this.myList = list;
}
#Override
public int getContentItemsTotal() {
return expanded? list.size() : 0;
}
#Override
public RecyclerView.ViewHolder getHeaderViewHolder(View view) {
return new HeaderViewHolder(view);
}
#Override
public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) {
final HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
headerHolder.tvTitle.setText(title);
headerHolder.rootView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
expanded = !expanded;
headerHolder.imgArrow.setImageResource(
expanded ? R.drawable.ic_keyboard_arrow_up_black_18dp : R.drawable.ic_keyboard_arrow_down_black_18dp
);
sectionAdapter.notifyDataSetChanged();
}
});
}
#Override
public RecyclerView.ViewHolder getItemViewHolder(View view) {
// return a custom instance of ViewHolder for the items of this section
return new MyItemViewHolder(view);
}
#Override
public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
MyItemViewHolder itemHolder = (MyItemViewHolder) holder;
// bind your view here
itemHolder.tvItem.setText(list.get(position));
}
}
Then create instance of your sections and set up your adapter:
// Create an instance of SectionedRecyclerViewAdapter
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();
// Add your Sections
sectionAdapter.addSection(new MySection("A", Arrays.asList(new String[] {"a", "b", "c" })));
sectionAdapter.addSection(new MySection("B", Arrays.asList(new String[] {"d", "e", "f" })));
// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);

Categories

Resources