Recycleview realizes unlimited automatic carousel
- 2021-12-04 19:50:21
- OfStack
Overview
In my opinion, there are two ways for RecycleView to realize infinite repeated sliding of specific data
1. Modify the reuse mechanism of adpter to reuse data indefinitely
2. Return data length in adpter returns the maximum value of Integer
Because the first method can realize the infinite repetition of data, but the data bits still have no change, so it is impossible to carousel down one bit when automatically jumping to the end, so here I use the second method to realize automatic carousel
Briefly describe the reuse mechanism of modifying adpter
Let's take the linear LinearLayoutManager as an example. We only need to reconstruct LinearLayoutManager and do some hands and feet when drawing it.
package com.li.liproject.recycle;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;
/**
* @author Version: 1.0
* Creation date: 2020/4/14 14
* Description:
*/
public class ScrollSpeedLinearLayoutManger extends LinearLayoutManager {
public ScrollSpeedLinearLayoutManger(Context context) {
super(context);
}
public ScrollSpeedLinearLayoutManger(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public ScrollSpeedLinearLayoutManger(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
}
// 1 In RecyclerView When initialized, it is called twice.
// 2 When calling adapter.notifyDataSetChanged() Is called when the.
// 3 When calling setAdapter Replace Adapter Hour , Will be called.
// 4 In RecyclerView It is also called when the animation is executed.
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
Log.d("TAG","onLayoutChildren ");
if (getItemCount() == 0){
detachAndScrapAttachedViews(recycler);
return;
}
//state.isPreLayout() Is to support animation
if (getItemCount() == 0 && state.isPreLayout()){
return;
}
// Will the current Recycler In view Remove all of them and put them in the scrap cache , After that, priority is given to reusing the view
detachAndScrapAttachedViews(recycler);
int actualHeight = 0;
for (int i = 0 ;i < getItemCount() ; i++){
View scrap = recycler.getViewForPosition(i);
addView(scrap);
measureChildWithMargins(scrap,0,0);
int width = getDecoratedMeasuredWidth(scrap);
int height = getDecoratedMeasuredHeight(scrap);
layoutDecorated(scrap,0,actualHeight,width,actualHeight+height);
actualHeight+=height;
// If it goes beyond the interface, it will not be drawn , Nor is it add It's over
if (actualHeight > getHeight()){
break;
}
}
}
@Override
public boolean canScrollVertically() {
return true;
}
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
Log.d("feifeifei","getChildCount() " + getChildCount() + " recycler.getScrapList().size() " + recycler.getScrapList().size());
// When the interface scrolls down, ,dy Is positive , When scrolling up, dy Is negative
// Padding
fill(dy,recycler,state);
// Scroll
offsetChildrenVertical(dy*-1);
// Recycle the that has left the interface
recycleOut(dy,recycler,state);
return dy;
}
private void fill(int dy, RecyclerView.Recycler recycler, RecyclerView.State state){
// Scroll down
if (dy > 0){
// Fill in the bottom first
View lastView = getChildAt(getChildCount() -1);
int lastPos = getPosition(lastView);
if (lastView.getBottom() - dy < getHeight()){
View scrap;
if (lastPos == getItemCount() -1){
scrap = recycler.getViewForPosition(0);
}else {
scrap = recycler.getViewForPosition(lastPos+1);
}
addView(scrap);
measureChildWithMargins(scrap,0,0);
int width = getDecoratedMeasuredWidth(scrap);
int height = getDecoratedMeasuredHeight(scrap);
layoutDecorated(scrap,0,lastView.getBottom(),width,lastView.getBottom()+height);
}
}else {
// Scroll up
// Now fill the top
View firstView = getChildAt(0);
int layoutPostion = getPosition(firstView);
if (firstView.getTop() >= 0 ){
View scrap ;
if (layoutPostion == 0){
scrap = recycler.getViewForPosition(getItemCount()-1);
}else {
scrap = recycler.getViewForPosition(layoutPostion -1);
}
addView(scrap,0);
measureChildWithMargins(scrap,0,0);
int width = getDecoratedMeasuredWidth(scrap);
int height = getDecoratedMeasuredHeight(scrap);
layoutDecorated(scrap,0,firstView.getTop() - height,width,firstView.getTop());
}
}
}
private void recycleOut(int dy, RecyclerView.Recycler recycler, RecyclerView.State state){
for (int i = 0 ; i <getChildCount() ;i++){
View view = getChildAt(i);
if (dy >0){
if (view.getBottom()-dy <0){
Log.d("feifeifei","recycleOut " + i);
removeAndRecycleView(view,recycler);
}
}else {
if (view.getTop()-dy > getHeight()){
Log.d("feifeifei","recycleOut " + i);
removeAndRecycleView(view,recycler);
}
}
}
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext());
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
private class CenterSmoothScroller extends LinearSmoothScroller {
public CenterSmoothScroller(Context context) {
super(context);
}
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return 0.2f;
}
}
}
This is probably how the grid needs to be rewritten because the calculation will be different. Here is a simple description of 1
Get down to business
Implementation of Adpter Adapter
There is no difference in writing. Only getItemCount and onBindViewHolder with 1 should be processed as follows
public class AdAuditorAdapter extends RecyclerView.Adapter<AdAuditorAdapter.MyViewHolder> {
private Context mContext;
private List<String> mData;
public AdAuditorAdapter(Context mContext, List<String> mData) {
this.mContext = mContext;
this.mData = mData;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
MyViewHolder holder = new MyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_adauditor, viewGroup, false));
return holder;
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder myViewHolder, @SuppressLint("RecyclerView") int i) {
Log.e("HHHHHHHHH", "onBindViewHolder: -->"+i );
// Take the remainder or the index will be out of bounds
myViewHolder.tv_1.setText(mData.get(i%mData.size()));
myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onClick(v, i%mData.size(), 3);
}
});
}
@Override
public int getItemCount() {
// Return adpter Maximum value
return Integer.MAX_VALUE;
}
public void update(List<String> list) {
this.mData = list;
notifyDataSetChanged();
}
class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv_1;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
tv_1 = itemView.findViewById(R.id.tv_1); //ID
}
}
public MyClickListener getListener() {
return listener;
}
public void setMyClickListener(MyClickListener listener) {
this.listener = listener;
}
MyClickListener listener;
public interface MyClickListener {
void onClick(View view, int position, int type);
}
public List<String> getData() {
return mData;
}
}
Implementation of activity
1 basic implementation
1.1 Add false data and write click events
1.2 Delay message mRecyclerView. smoothScrollToPosition (position) with handler; Move to the specified position
1.3 Click Stop Moving
2 Effect optimization
2.1 Add uniform damping effect
2.2 Realize infinite carousel considering the situation that the value exceeds the maximum value of Integer
2.3 Clicking recycleview while carousel will stop carousel, and clicking again will execute click event (optimized to click stop and execute click event)
The damping effect is to reduce the sliding rate
Let's do this
package com.li.liproject.recycle;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;
/**
* @author Version: 1.0
* Creation date: 2020/4/14 14
* Description:
*/
public class ScrollSpeedGridLayoutManager1 extends GridLayoutManager {
public ScrollSpeedGridLayoutManager1(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public ScrollSpeedGridLayoutManager1(Context context, int spanCount) {
super(context, spanCount);
}
public ScrollSpeedGridLayoutManager1(Context context, int spanCount, int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext());
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
private class CenterSmoothScroller extends LinearSmoothScroller {
public CenterSmoothScroller(Context context) {
super(context);
}
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return 10f;// Sliding rate problem
}
}
}
activity full code
package com.li.liproject.recycle;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.li.liproject.MainActivity;
import com.li.liproject.R;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
/**
* @author Version: 1.0
* Creation date: 2020/4/14 14
* Description:
*/
public class RecycleViewActivity extends AppCompatActivity {
static RecyclerView rv_1;
private static int HANDLER_MSG = 0x0011;
private static int HANDLER_LONG_MSG = 0x0021;
static int position = 0;
static int addNum = 3;
@SuppressLint("HandlerLeak")
private static Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
if (msg.what == HANDLER_MSG) {
// 9 The realization rate of Gongge effect is the same
if (addNum==3){
position = position + addNum;
addNum = 6;
}else {
position = position + addNum;
addNum = 3;
}
Log.e("TAG", "handleMessage: -->" + position);
smoothMoveToPosition(rv_1, position >= 0 ? position : 0);
if (position==Integer.MAX_VALUE/2){
// Click or exceed 2 Divide Integer.MAX_VALU Reset adpter
LongAutoMove();
}else {
AutoMove();
}
}else if (msg.what==HANDLER_LONG_MSG){
position = 0;
addNum = 3;
Log.e("TAG", "handleMessage: -->" + position);
smoothMoveToPosition(rv_1, 0);
AutoMove();
}
}
};
private static AdAuditorAdapter adAuditorAdapter;
static List<String> strings;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycle);
rv_1 = findViewById(R.id.rv_1);
strings = new ArrayList<>();
for (int i = 0; i < 50; i++) {
strings.add(i + "");
}
adAuditorAdapter = new AdAuditorAdapter(this, strings);
adAuditorAdapter.setMyClickListener(new AdAuditorAdapter.MyClickListener() {
@Override
public void onClick(View view, int position, int type) {
Toast.makeText(RecycleViewActivity.this, adAuditorAdapter.getData().get(position), Toast.LENGTH_SHORT).show();
StopMove();
}
});
GridLayoutManager layoutManager = new ScrollSpeedGridLayoutManager1(this,3,GridLayoutManager.HORIZONTAL, false);
rv_1.setLayoutManager(layoutManager);
rv_1.setAdapter(adAuditorAdapter);
rv_1.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
// if (mShouldScroll && RecyclerView.SCROLL_STATE_IDLE == newState) {
// mShouldScroll = false;
// smoothMoveToPosition(recyclerView, mToPosition);
// }
Log.e("TAG", "onScrollStateChanged11111111: -->" + newState);
if (newState == 1) {
// RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder(recyclerView.getRootView());
recyclerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(RecycleViewActivity.this, adAuditorAdapter.getData().get(position)+"........", Toast.LENGTH_SHORT).show();
}
});
StopMove();
LongAutoMove();
}
}
// @Override
// public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
// super.onScrolled(recyclerView, dx, dy);
// Log.e("TAG", "onScrolled: dx=" +dx +" dy="+dy );
// }
});
rv_1.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction()==MotionEvent.ACTION_DOWN){
// Monitoring click location found view Implement click events
View childView = rv_1.findChildViewUnder(event.getX(), event.getY());
Log.e("GGGGGGGGGGGGGGGGG", "onTouch: -->"+rv_1.getChildLayoutPosition(childView));
adAuditorAdapter.getListener().onClick(v, rv_1.getChildLayoutPosition(childView),1);
}
return false;
}
});
AutoMove();
}
private static void AutoMove() {
handler.removeMessages(HANDLER_MSG);
handler.sendEmptyMessageDelayed(HANDLER_MSG, 2000);
}
private static void LongAutoMove() {
if (handler.hasMessages(HANDLER_MSG)) {
handler.removeMessages(HANDLER_LONG_MSG);
}
handler.sendEmptyMessageDelayed(HANDLER_LONG_MSG, 5000);
}
public static void StopMove() {
if (handler.hasMessages(HANDLER_MSG)) {
handler.removeMessages(HANDLER_MSG);
}
}
// Whether the target item is last 1 After visible items
private static boolean mShouldScroll;
// Record the location of the target item
private static int mToPosition;
/**
* Slide to the specified position
*/
private static void smoothMoveToPosition(RecyclerView mRecyclerView, final int position) {
if (position==0){
mRecyclerView.setAdapter(adAuditorAdapter);
}
mRecyclerView.smoothScrollToPosition(position);
mToPosition = position;
mShouldScroll = true;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (handler!=null){
handler.removeCallbacksAndMessages(null);
handler= null;
}
if (adAuditorAdapter!=null) {
adAuditorAdapter= null;
}
}
}
Basic realization of automatic carousel effect
Here's Demo only written about the effect there are a lot of things need to be optimized under 1, in order to get the project to use