Search code examples
androidandroid-coordinatorlayout

Smooth scrolling for Appbarlayout and recyclerview


I want to achieve smooth scrolling behavior like Google Play App. I have tried solutions mentioned in here:

  1. solution 1
  2. solution 2
  3. solution 3

But all above solution didn't gave expected result as in Google Play App. Here is xml code:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layoutContent"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

<android.support.design.widget.AppBarLayout
    android:id="@+id/appBarLayout"
    android:layout_width="match_parent"
    android:layout_height="250dp"
    android:background="@android:color/transparent"
    app:elevation="0dp">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsingToolbar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:contentScrim="?attr/colorPrimary"
        app:layout_scrollFlags="scroll|exitUntilCollapsed">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <View
                android:id="@+id/view1"
                android:layout_width="match_parent"
                android:layout_height="125dp"
                android:background="@android:color/transparent" />

            <android.support.v4.view.ViewPager
                android:id="@+id/pager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                android:paddingLeft="24dp"
                android:paddingRight="12dp" />
        </RelativeLayout>
    </android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentEnd="true"
    android:layout_alignParentRight="true"
    android:layout_margin="@dimen/fab_margin"
    app:backgroundTint="@android:color/white"
    app:layout_anchor="@id/recycler_view"
    app:layout_anchorGravity="bottom|right|end"
    app:srcCompat="@drawable/ic_add_card" />
</android.support.design.widget.CoordinatorLayout>

How to achieve smooth scrolling?

Edit: Here is recyclerview adapter

class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewViewHolder> {
private static final int DAYS_LEFT_TO_EXPIRE = 3;
private CardCallback mCallback;
private List<Ticket> mTickets;

@Inject
RecyclerViewAdapter() {
    mTickets = new ArrayList<>();
}

@Override
public RecyclerViewViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_card, parent, false);
    return new RecyclerViewViewHolder(view);
}

@Override
public void onBindViewHolder(final RecyclerViewViewHolder holder, int position) {
    Ticket ticket = mTickets.get(position);
    if (ticket != null) {
        holder.setTicket(ticket);
        if (ticket.getHeadCount() != -1)
            holder.mTxtHeadCount.setText(String.valueOf(ticket.getHeadCount()));
        if (ticket.getType() != null && !ticket.getType().trim().isEmpty())
            setCardTitle(holder.mTxtCardTitle, ticket);
        if (ticket.getBlockCount() != -1)
            holder.mTxtBlockCount.setText(String.valueOf(ticket.getBlockCount()));
        if (ticket.getSeasonalPass() != null && !ticket.getSeasonalPass().trim().isEmpty())
            holder.mCardSeasonalPass.setText(ticket.getSeasonalPass());
        if (ticket.getLastUsedDate() != null)
            holder.mTxtCardLastUsedDate.setText(ticketDateFormatter.format(ticket.getLastUsedDate()));
        if (ticket.getExpiryDate() != null)
            holder.mTxtCardExpiryDate.setText(ticketDateFormatter.format(ticket.getExpiryDate()));
        if (ticket.getSeatingClass() != null && !ticket.getSeatingClass().trim().isEmpty())
            holder.mTxtSeatingClass.setText(ticket.getSeatingClass());
        if (ticket.getTheaterGroup() != null && !ticket.getTheaterGroup().trim().isEmpty())
            holder.mTxtTheaterGroup.setText(ticket.getTheaterGroup());
        if (ticket.getSellerName() != null && !ticket.getSellerName().trim().isEmpty())
            holder.mTxtSellerName.setText(ticket.getSellerName());
        if (ticket.getDescription() != null && !ticket.getDescription().trim().isEmpty())
            holder.mTxtCardDescription.setText(ticket.getDescription());
        if (ticket.getStatus() != null && !ticket.getStatus().trim().isEmpty())
            holder.mTxtCardStatus.setText(ticket.getStatus());
        if (DateUtil.noOfDaysLeft(ticket.getExpiryDate()) <= DAYS_LEFT_TO_EXPIRE)
            holder.mLblWarningMessage.setVisibility(View.VISIBLE);
        else holder.mLblWarningMessage.setVisibility(View.GONE);
        setCardProgress(holder.mCardProgress, ticket);
    }
}

private void setCardTitle(TextView txtCardTitle, Ticket ticket) {
    switch (ticket.getType()) {
        case Constants.CARD_TYPE_PACK:
            txtCardTitle.setText(ticket.getPackCount() + " " + ticket.getType());
            break;
        case Constants.CARD_TYPE_SEASONAL:
            txtCardTitle.setText(ticket.getType().concat(" pass"));
            break;
        default:
            txtCardTitle.setText(ticket.getType());
            break;
    }
}

private void setCardProgress(ProgressBar progressBar, Ticket ticket) {
    int maxLimit = 0, progress = 0;
    switch (ticket.getType()) {
        case Constants.CARD_TYPE_SEASONAL:
            maxLimit = (int) TimeUnit.MILLISECONDS.toDays(ticket.getExpiryDate().getTime() - ticket.getLastUsedDate().getTime());
            progress = maxLimit - DateUtil.noOfDaysLeft(ticket.getExpiryDate());
            break;
        case Constants.CARD_TYPE_PACK:
            maxLimit = ticket.getPackCount();
            progress = maxLimit - (ticket.getPackCount() - ticket.getPackUsed());
            break;
        case Constants.CARD_TYPE_TICKET:
            maxLimit = (int) TimeUnit.MILLISECONDS.toDays(ticket.getExpiryDate().getTime() - ticket.getTicketBoughtDate().getTime());
            progress = maxLimit - DateUtil.noOfDaysLeft(ticket.getExpiryDate());
            break;
    }
    progressBar.setMax(maxLimit);
    if (progress < 0) {
        progressBar.setProgress(maxLimit);
    } else {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
            progressBar.setProgress(progress, true);
        else progressBar.setProgress(progress);
    }
}

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

void setTickets(List<Ticket> tickets) {
    if (tickets != null && !tickets.isEmpty()) {
        mTickets.addAll(tickets);
        notifyDataSetChanged();
    }
}

void setCallback(CardCallback callback) {
    this.mCallback = callback;
}

void clearTicketList() {
    mTickets.clear();
    notifyItemRangeChanged(0, mTickets.size());
}

interface CardCallback {
    void onBtnMoreCardInfoClicked(Ticket ticket, LinearLayout layout, ImageButton imageButton);
}

class RecyclerViewViewHolder extends RecyclerView.ViewHolder {
    @BindView(R.id.txt_head_count)
    TextView mTxtHeadCount;
    @BindView(R.id.txt_card_type)
    TextView mTxtCardTitle;
    @BindView(R.id.txt_block_count)
    TextView mTxtBlockCount;
    @BindView(R.id.txt_seasonal_pass)
    TextView mCardSeasonalPass;
    @BindView(R.id.txt_card_last_used_date)
    TextView mTxtCardLastUsedDate;
    @BindView(R.id.txt_card_expiry_date)
    TextView mTxtCardExpiryDate;
    @BindView(R.id.card_progress)
    ProgressBar mCardProgress;
    @BindView(R.id.txt_seating_class)
    TextView mTxtSeatingClass;
    @BindView(R.id.txt_theater_group)
    TextView mTxtTheaterGroup;
    @BindView(R.id.txt_seller_name)
    TextView mTxtSellerName;
    @BindView(R.id.btn_more_card_info)
    ImageButton mBtnMoreCardInfo;
    @BindView(R.id.layout_card_description)
    LinearLayout mLayoutCardDescription;
    @BindView(R.id.txt_card_description)
    TextView mTxtCardDescription;
    @BindView(R.id.txt_card_status)
    TextView mTxtCardStatus;
    @BindView(R.id.lbl_warning_message)
    TextView mLblWarningMessage;

    private Ticket mTicket;

    NFCCardRecyclerViewViewHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);

        mBtnMoreCardInfo.setOnClickListener(v -> {
            if (mCallback != null)
                mCallback.onBtnMoreCardInfoClicked(mTicket, mLayoutCardDescription, mBtnMoreCardInfo);
        });
    }

    void setTicket(Ticket mTicket) {
        this.mTicket = mTicket;
    }
}

}


Solution

  • follow steps on this link: smooth app bar layout This library helped me solving my problem.