2

Please don't mark it as a duplicate of this.

I am trying Collapsable CardView but the last card when expanded doesn't push its content upwards, rather the user has to explicitly scroll down. I have tried the mentioned ways but all in vain. This is happening.

enter image description here

This might be achieved using some library but I don't prefer it. I think I need to get the position of the current card and then apply logic.

Here's my implementation of the Adapter:

public class RVAdapter extends RecyclerView.Adapter<MyViewHolder> {

private ArrayList<AppItem> appItemList;
private Context context;

RVAdapter(ArrayList<AppItem> appItemList, Context context) {
    this.appItemList = appItemList;
    this.context = context;
}

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View layoutView = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.card_list_item, parent, false);
    return new MyViewHolder(layoutView);
}

@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
    final AppItem item = appItemList.get(position);
    holder.description.setText(item.getDescription());
    holder.title.setText(item.getTitle());
    holder.appImage.setBackgroundResource(item.getImageID());

    holder.button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

        }
    });
}

@Override
public int getItemCount() {
    return this.appItemList.size();
}
}

The ViewHolder:-

class MyViewHolder extends RecyclerView.ViewHolder {

ImageView appImage;
TextView title;
TextView description;
Button button;

MyViewHolder(final View itemView) {
    super(itemView);
    this.appImage = itemView.findViewById(R.id.image);
    this.title = itemView.findViewById(R.id.title);
    this.description = itemView.findViewById(R.id.description);
    this.button = itemView.findViewById(R.id.button);

    itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            LinearLayout expandableView = view.findViewById(R.id.expandable_view);
            expandableView.setVisibility(expandableView.getVisibility() == View.GONE ?
            View.VISIBLE : View.GONE);
        }
    });
}

}
Rohn
  • 95
  • 2
  • 13

2 Answers2

1

Use can use this method inside on create of your Activity/fragment:

private void bindView() {
    mRecyclerView.setLayoutManager(mLayoutManager);
    mRecyclerView.setNestedScrollingEnabled(false);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    mRecyclerView.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), mRecyclerView, new RecyclerTouchListener.ClickListener() {
        @Override
        public void onClick(final View view, final int position)
        {
            if (mRecyclerView != null)
            {


                handler.postDelayed(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        scrollToView(scrollView,view);
                    }
                },500);
            }

        }

        @Override
        public void onLongClick(View view, int position)
        {

        }
    }));
}

Related methods called:

/** scrolling items as per child inside child**/
private void scrollToView(final ScrollView scrollViewParent, final View view) {
    // Get deepChild Offset
    Point childOffset = new Point();
    getDeepChildOffset(scrollViewParent, view.getParent(), view, childOffset);
    // Scroll to child.
    scrollViewParent.smoothScrollTo(0, childOffset.y);
}
private void getDeepChildOffset(final ViewGroup mainParent, final ViewParent parent, final View child, final Point accumulatedOffset) {
    ViewGroup parentGroup = (ViewGroup) parent;
    accumulatedOffset.x += child.getLeft();
    accumulatedOffset.y += child.getTop();
    if (parentGroup.equals(mainParent)) {
        return;
    }
    getDeepChildOffset(mainParent, parentGroup.getParent(), parentGroup, accumulatedOffset);
}

class RecyclerTouchListener:

public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {

private GestureDetector gestureDetector;
private ClickListener clickListener;

public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
    this.clickListener = clickListener;
    gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
            if (child != null && clickListener != null) {
                clickListener.onLongClick(child, recyclerView.getChildPosition(child));
            }
        }
    });
}

@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {

    View child = rv.findChildViewUnder(e.getX(), e.getY());
    if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
        clickListener.onClick(child, rv.getChildPosition(child));
    }
    return false;
}

@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

}

public interface ClickListener {
    void onClick(View view, int position);

    void onLongClick(View view, int position);
}}

Try this out and let me know in comments

nimi0112
  • 2,065
  • 1
  • 18
  • 32
  • In `scrollToView` method inside handler how to use ScrollView? I am not using ScrollView! – Rohn Mar 12 '18 at 17:06
  • 1
    @Rohn Just make the parent layout as Scrollview in your activity/fragment then you are good to go, let me know if you need any help. This is a working solution and I ma using it in my app. – nimi0112 Mar 13 '18 at 06:39
0

Take a look at requestChildRectangleOnScreen of RecyclerView.LayoutManager to see if it does what you need.

requestChildRectangleOnScreen

Requests that the given child of the RecyclerView be positioned onto the screen.

Here is some sample code that will move the last visible child of a RecyclerView on screen once the RecyclerView is idle. This is not exactly what you are looking for, but you can adapt it to your needs.

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
            LinearLayoutManager llm = (LinearLayoutManager) recyclerView.getLayoutManager();
            int lastPos = llm.findLastVisibleItemPosition();
            View lastView = llm.findViewByPosition(lastPos);
            Rect rect = new Rect(0, 0, lastView.getWidth(), lastView.getHeight());
            llm.requestChildRectangleOnScreen(recyclerView, lastView, rect, false);
        }
    }
});
Community
  • 1
  • 1
Cheticamp
  • 61,413
  • 10
  • 78
  • 131