English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
RecyclerView has been around for a long time, but in the project before, we always used ListView, and recently, in the new project, a lot of RecycleView has been used. Especially the pull-down refresh of waterfall, there is no suitable one on the Internet, so I summed up a little bit myself.
Let's take a look at the picture first:
There are two ways for me to implement the functions of pull-up loading more and pull-down refresh using RecyclerView:
1to use the system-provided Android.support.v4to implement this control.
2.Custom control containing RecyleView.
It is not easy to add a header using RecycleView, before using listview, you could add header and bottom yourself, but RecycleView seems not so easy to operate. For the first one, using the system-provided Android.support.v4It is implemented using .widget.SwipeRefreshLayout, which is also very useful, but the product generally does not want this kind of pull-down refresh. In order to appear awesome, he usually makes his own with animation, which is quite absurd... So we can only use the method2.
Briefly describe the method2Implementation method, the parent layout is ViewGroup, add View first, the first control is header, the second control is RecycleView, and the pull-down loading more view at the bottom is added through the Adapter of RecycleViw.
With the idea in mind, let's get started:
package com.krain.srecyclerview.fruitview; import android.content.Context; import android.graphics.drawable.AnimationDrawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; import com.krain.srecyclerview.R; /** * Créé par dafuShao le 2016/9/9 0009. * */ public class ElizabethView extends FrameLayout { private ImageView imageView; private AnimationDrawable animationDrawable; public ElizabethView(Context context) { super(context); initview(context); } public ElizabethView(Context context, AttributeSet attrs) { super(context, attrs); initview(context); } public ElizabethView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initview(context); } private void initview(Context context){ View view= LayoutInflater.from(context).inflate(R.layout.elizabeth_item,null); imageView=(ImageView) view.findViewById(R.id.elizabeth_im); animationDrawable= (AnimationDrawable) imageView.getBackground(); addView(view); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } //开始动画 public void startAnim(){ animationDrawable.start(); } //停止动画 public void stopAnim(){ animationDrawable.stop(); } }
这是头部控件很简单,里面有一个小的小眼睛左右挤一挤的效果,就不贴图了。
以下为包含RecyclerView的自定义控件代码如下:
package com.krain.srecyclerview.srecyclerview; import android.content.Context; import android.os.Handler; import android.os.Message; import android.support.v4.view.MotionEventCompat; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.Scroller; import android.widget.TextView; import com.krain.srecyclerview.R; import com.krain.srecyclerview.fruitview.ElizabethView; public class SRecyclerView extends ViewGroup { Context context; RecyclerView mRecyclerView; ElizabethView mHeaderView; TextView mFootViewTips;//affichage du texte de footview AdapterWrapper mAdapter; boolean mIsTop = true;//est-ce que le défilement est arrivé en haut de la page RecyclerView.LayoutManager mLayoutManager; int mLastVisibleItem; int mFirstVisibleItem; OnRecyclerStatusChangeListener mRecyclerChangeListener; int mStatus;//état actuel int mHeadviewHeight;//hauteur de headview Scroller mScroller; int mFristScollerY;//最开始的getscrolly boolean mHasFooter;//是否有上拉加载的功能 boolean mShowFootVisible;//是否显示FOOTERview在mHasFooter为true的时候生效 boolean mHasRefresh = true;//是否支持下拉刷新 private final int DEFAULT_MIN_PAGEINDEX = 1;//默认最小的页数 int mMaxPage = DEFAULT_MIN_PAGEINDEX;//分页的总页数 int mCurrentPage = DEFAULT_MIN_PAGEINDEX;//当前的页数,从1开始 private final int STATUS_NORMAL = 0, STATUS_REFRESH = 1, STATUS_LOAD = 2; private final int MSG_LOAD_COMPLETE = 1, MSG_REFRESH_COMPLETE = 0;//handle的常量 private final int DELAY_LOAD_COMPLETE = 1000, DELAY_REFRESH_COMPLETE = 1000;//加载完成延时回收的时间 public SRecyclerView(Context context) { super(context); init(context); } public SRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public SRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } /** * 设置最大页数 * * @param maxPage */ public void setMaxPage(int maxPage) { this.mMaxPage = maxPage; } /** * 是否支持上拉加载 * * @param hasLoadmore */ public void setLoadmore(boolean hasLoadmore) { mHasFooter = hasLoadmore; } public void setRecyclerViewLayoutManage(RecyclerView.LayoutManager mLayoutManage){ this.mLayoutManager=mLayoutManage; } /** * Désactiver la fonction de rafraîchissement en glissant vers le bas */ public void disableRefresh() { mHasRefresh = false; } public void setAdapter(BaseRecyclerViewAdapter adapter) { int height = 0; if (mMaxPage == DEFAULT_MIN_PAGEINDEX) { mHasFooter = false; } mAdapter = new AdapterWrapper(context, adapter); mRecyclerView.setAdapter(mAdapter); } private int getViewHeight(View view) { int measure = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); view.measure(measure, measure); return view.getMeasuredHeight(); } /** * Obtenir RecyclerView à l'intérieur du viewgroup * * @return */ public RecyclerView getRecyclerView() { return mRecyclerView; } public void setOnRecyclerChangeListener(OnRecyclerStatusChangeListener listener) { mRecyclerChangeListener = listener; } /** * Définir l'animation pour l'ajout et la suppression d'items dans RecyclerView * * @param animator */ public void setItemAnimator(RecyclerView.ItemAnimator animator) { mRecyclerView.setItemAnimator(animator); } public void notifyDataSetChanged() { mStatus = STATUS_NORMAL;//Réinitialiser les données pour représenter la fin de loadmore, à ce moment恢复成普通状态 mAdapter.notifyDataSetChanged(); } public void notifyDataInsert(int positionStart, int itemCount) { mStatus = STATUS_NORMAL;//Réinitialiser les données pour représenter la fin de loadmore, à ce moment恢复成普通状态 mAdapter.notifyItemRangeInserted(positionStart, itemCount); } public void notifyDataRemove(int position) { mStatus = STATUS_NORMAL;//Réinitialiser les données pour représenter la fin de loadmore, à ce moment恢复成普通状态 mAdapter.notifyItemRemoved(position); } /** * Opérations d'initialisation * * @param context */ void init(Context context) { this.context = context; mScroller = new Scroller(context); // if (mLayoutManager!=null){ // addChildView(context,mLayoutManager); // } // addChildView(context, new LinearLayoutManager(context)); // } addChildView(context); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } } /** * Ajouter un View enfant * * @param context */ void addChildView(Context context, RecyclerView.LayoutManager mLayoutManager) { ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mHeaderView = new ElizabethView(context); mRecyclerView = new RecyclerView(context); addView(mHeaderView); addView(mRecyclerView); //mLayoutManager = new LinearLayoutManager(context); // mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(mLayoutManager); // Définir l'animation par défaut pour l'ajout et la suppression des éléments mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mRecyclerView.addOnScrollListener(onScrollListener); mRecyclerView.setHasFixedSize(true); mRecyclerView.setLayoutParams(params); } void addChildView(Context contex) { ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mHeaderView = new ElizabethView(context); mRecyclerView = new RecyclerView(context); addView(mHeaderView); addView(mRecyclerView); //mLayoutManager = new LinearLayoutManager(context); mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(mLayoutManager); // Définir l'animation par défaut pour l'ajout et la suppression des éléments mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mRecyclerView.addOnScrollListener(onScrollListener); mRecyclerView.setHasFixedSize(true); mRecyclerView.setLayoutParams(params); } /** * Écarter les événements de touche de Recyclerview (lors du rafraîchissement en descendant) */ float lastY; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); switch (action) { case MotionEvent.ACTION_DOWN: lastY = ev.getRawY(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: return false; case MotionEvent.ACTION_MOVE: if (mHasRefresh && mIsTop && ev.getRawY() > lastY) return true; break; } return false; } float offsetY = 0; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: offsetY = Math.abs(event.getRawY() - lastY); //Valeur absolue de la différence Y if (offsetY > 0) scrollToOffset(offsetY); else { mIsTop = false; } break; case MotionEvent.ACTION_UP: if (getScrollY() <= 0) { doRefresh(); mHeaderView.stopAnim(); mHeaderView.startAnim(); } else complete(); break; } return super.onTouchEvent(event); } /** * Faire glisser ce view jusqu'à la position glissée * * @param offsetY Quantité de déplacement en Y */ void scrollToOffset(float offsetY) { //Si le rafraîchissement est en cours et que la valeur actuelle de scrolly est identique à la valeur initiale, cela signifie que la descente pour le rafraîchissement est prête à commencer et qu'une seule exécution de only est effectuée if (getScrollY() == mFristScollerY && mRecyclerChangeListener != null) mRecyclerChangeListener.startRefresh(); int value = Math.round(offsetY / 2.0F); value = mFristScollerY - value; scrollTo(0, value); } /** * Effectuer l'opération de rafraîchissement, déplacer vers la position du header qui vient de sortir */ void doRefresh() { mStatus = STATUS_REFRESH; int currentY = getScrollY(); mScroller.startScroll(0, currentY, 0, (mFristScollerY - mHeadviewHeight) - currentY); invalidate(); if (mRecyclerChangeListener != null) mRecyclerChangeListener.onRefresh(); handler.sendEmptyMessageDelayed(MSG_REFRESH_COMPLETE, DELAY_REFRESH_COMPLETE); } Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == MSG_LOAD_COMPLETE) { View footview = mAdapter.getFootView(); if (footview != null) mRecyclerView.smoothScrollBy(0, -footview.getMeasuredHeight()); } else if (msg.what == MSG_REFRESH_COMPLETE) complete(); } }; /** * Le header revient à sa place et est complètement caché */ public void complete() { mCurrentPage = DEFAULT_MIN_PAGEINDEX;//Après la fin, le page actuel est restauré à la valeur par défaut if (mFootViewTips != null) mFootViewTips.setText(context.getString(R.string.loading));//Modifier l'indication du pied en cours de chargement if (mRecyclerChangeListener != null) mRecyclerChangeListener.refreshComplete(); mStatus = STATUS_NORMAL; int currentY = getScrollY(); mScroller.startScroll(0, currentY, 0, mFristScollerY - currentY); mHeaderView.stopAnim(); invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); measureChild(child, widthMeasureSpec, heightMeasureSpec); height += child.getMeasuredHeight(); } setMeasuredDimension(width, height); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int left = getPaddingLeft(); int top = getPaddingTop(); for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (i == 0) {//Affichage centré lorsque c'est le header int headerLeft = getMeasuredWidth(); / 2 - child.getMeasuredWidth() / 2; child.layout(headerLeft, top, headerLeft + child.getMeasuredWidth(), top + child.getMeasuredHeight()); } else child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()); top += child.getMeasuredHeight(); } mHeadviewHeight = getPaddingTop(); + mHeaderView.getMeasuredHeight(); scrollTo(0, mHeadviewHeight);//Déplacez-vous sous le header pour afficher le recyleview mFristScollerY = getScrollY(); } /** * Écouteur d'événement de défilement de RecyclerView */ RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_IDLE) { //On a glissé jusqu'au sommet if (mFirstVisibleItem == 0) { mIsTop = true; } else { mIsTop = false; if (mStatus != STATUS_LOAD && mShowFootVisible && mLastVisibleItem + 1 == mAdapter.getItemCount()) { if (mCurrentPage == mMaxPage) { //Lorsque la page actuelle est la dernière page mFootViewTips = (TextView) mAdapter.getFootView().findViewById(R.id.footer_tips); mFootViewTips.setText(context.getString(R.string.last_page_tips)); handler.sendEmptyMessageDelayed(MSG_LOAD_COMPLETE, DELAY_LOAD_COMPLETE); } else { mStatus = STATUS_LOAD; if (mRecyclerChangeListener != null) { mRecyclerChangeListener.onLoadMore(); mCurrentPage++; } } } } } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); if (mLayoutManager instanceof LinearLayoutManager) { mLastVisibleItem = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition(); mFirstVisibleItem = ((LinearLayoutManager) mLayoutManager).findFirstCompletelyVisibleItemPosition(); setFootviewVisible(); } else if (mLayoutManager instanceof GridLayoutManager) { mLastVisibleItem = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition(); mFirstVisibleItem = ((GridLayoutManager) mLayoutManager).findFirstCompletelyVisibleItemPosition(); setFootviewVisible(); } else if (mLayoutManager instanceof StaggeredGridLayoutManager) { //En raison de la particularité de StaggeredGridLayoutManager, il peut y avoir plusieurs éléments affichés en dernier, donc ici, nous obtenons un tableau. //Après avoir obtenu cet tableau, la valeur de position la plus grande dans le tableau est la valeur de position affichée en dernier. int[] lastPositions = new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()]; ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(lastPositions); mLastVisibleItem = findMax(lastPositions); mFirstVisibleItem = ((StaggeredGridLayoutManager) mLayoutManager).findFirstVisibleItemPositions(lastPositions)[0]; setFootviewVisible(); } } }; void setFootviewVisible() { //当设置了拥有上拉加载功能但是第一页的条目不足以盛满Recyclerview的时候隐藏footer if (mHasFooter && mFirstVisibleItem == 0) { /** * 这里加上一个mShowFootVisible在上拉加载功能启用的情况下生效,从来控制item数量不足铺满 * recyclerview高度的时刻隐藏footview,在item数量超过view高度的情况下显示 */ if (mLastVisibleItem + 1 == mAdapter.getItemCount()) { mShowFootVisible = false; } else mShowFootVisible = true; notifyDataSetChanged(); } } private int findMax(int[] positions) { int max = positions[0]; for (int value : positions) { if (value > max) { max = value; } } return max; } private class AdapterWrapper extends RecyclerView.Adapter { private static final int TYPE_ITEM = 0; private static final int TYPE_FOOTER = 1; private RecyclerView.Adapter mAdapter; private Context mContext; View footer; public AdapterWrapper(Context context, RecyclerView.Adapter wrappedAdapter) { this.mContext = context; this.mAdapter = wrappedAdapter; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { RecyclerView.ViewHolder holder = null; switch (viewType) { case TYPE_ITEM: holder = mAdapter.onCreateViewHolder(parent, viewType); break; case TYPE_FOOTER: footer = LayoutInflater.from(mContext).inflate(R.layout.lib_recyle_footview, null); LinearLayout linearLayout = (LinearLayout) footer.findViewById(R.id.loading_layout); StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); layoutParams.setFullSpan(true); linearLayout.setLayoutParams(layoutParams); holder = new FooterViewHolder(footer); break; } return holder; } public View getFootView() { return footer; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (!mHasFooter || position + 1 != getItemCount()) { mAdapter.onBindViewHolder(holder, position); } } @Override public int getItemCount() { return mShowFootVisible ? mAdapter.getItemCount() + 1 : mAdapter.getItemCount(); } @Override public int getItemViewType(int position) { if (mShowFootVisible && position + 1 == getItemCount()) { return TYPE_FOOTER; } else { return TYPE_ITEM; } } private class FooterViewHolder extends RecyclerView.ViewHolder { public ProgressBar progressBar; public TextView tvLoading; public LinearLayout llyLoading; public FooterViewHolder(View itemView) { super(itemView); progressBar = (ProgressBar) itemView.findViewById(R.id.progress_loading); tvLoading = (TextView) itemView.findViewById(R.id.footer_tips); llyLoading = (LinearLayout) itemView.findViewById(R.id.loading_layout); } } } }
Il reste encore le code de l'Adapter :
package com.krain.srecyclerview.srecyclerview; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; public abstract class BaseRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> { private OnItemClickLisener mItemListener; @Override public VH onCreateViewHolder(ViewGroup parent, int viewType) { View view = getItemView(viewType, parent); VH vh = getViewHolder(view); view.setOnClickListener(new OnRecyclerAdapterclickListener(vh, viewType)); view.setOnLongClickListener(new OnRecyclerAdapterclickListener(vh, viewType)); return vh; } public abstract VH getViewHolder(View itemView); /** * retourne la vue de l'élément * * @return */ public abstract View getItemView(int viewType, ViewGroup parent); /** * retourne les données de chaque élément de l'Adapter, optionnel */ public Object getItem(int position) { return null; } /** * interface d'événement de clic sur l'élément * * @param mItemListener */ public void setOnItemListener(OnItemClickLisener mItemListener) { this.mItemListener = mItemListener; } @Override public abstract void onBindViewHolder(VH holder, int position); @Override public abstract int getItemCount(); class OnRecyclerAdapterclickListener implements View.OnClickListener, View.OnLongClickListener { VH viewholder; int viewType; public OnRecyclerAdapterclickListener(VH viewholder, int viewType) { this.viewholder = viewholder; this.viewType = viewType; } @Override public void onClick(View v) { if (mItemListener != null && viewholder.getAdapterPosition() != RecyclerView.NO_POSITION) { mItemListener.onItemClick(viewholder.getAdapterPosition(), viewType, viewholder, v); } } @Override public boolean onLongClick(View v) { if (mItemListener != null && viewholder.getAdapterPosition() != RecyclerView.NO_POSITION) { mItemListener.onItemLongClick(viewholder.getAdapterPosition(), viewType, viewholder, v); } return false; } } }
Il faut noter un point :
Si vous souhaitez modifier le mode de mise en page de Reciview, faites cette modification ici;
void addChildView(Context contex) { ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mHeaderView = new ElizabethView(context); mRecyclerView = new RecyclerView(context); addView(mHeaderView); addView(mRecyclerView); //mLayoutManager = new LinearLayoutManager(context); mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(mLayoutManager); // Définir l'animation par défaut pour l'ajout et la suppression des éléments mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mRecyclerView.addOnScrollListener(onScrollListener); mRecyclerView.setHasFixedSize(true); mRecyclerView.setLayoutParams(params); }
mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); Cette ligne est là, il suffit de la modifier en conséquence. Ce n'est pas encore encapsulé, je vais l'encapsuler dans quelques jours.
Il faut aussi noter que lors du glissement, il faut vérifier s'il s'agit de la dernière ligne. Si c'est un cascade, il faut noter que la méthode de détermination est différente des autres.
if (mLayoutManager instanceof StaggeredGridLayoutManager) { //En raison de la particularité de StaggeredGridLayoutManager, il peut y avoir plusieurs éléments affichés en dernier, donc ici, nous obtenons un tableau. //Après avoir obtenu cet tableau, la valeur de position la plus grande dans le tableau est la valeur de position affichée en dernier. int[] lastPositions = new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()]; ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(lastPositions); mLastVisibleItem = findMax(lastPositions); mFirstVisibleItem = ((StaggeredGridLayoutManager) mLayoutManager).findFirstVisibleItemPositions(lastPositions)[0]; setFootviewVisible(); }
En raison de la particularité de StaggeredGridLayoutManager, il peut y avoir plusieurs éléments affichés en dernier, donc ici, nous obtenons un tableau. Une fois le tableau obtenu, nous obtenons la position la plus élevée de ce tableau, qui est la valeur de la position affichée en dernier. Ensuite, nous allons configurer l'affichage du chargement supplémentaire de la dernière ligne.
Ce que j'ai mentionné ci-dessus est la méthode d'implémentation de la fonction de chargement supplémentaire en haut et de mise à jour en bas de RecyclerView Android que j'ai présentée aux éditeurs. J'espère que cela vous sera utile. Si vous avez des questions, n'hésitez pas à me laisser un message, je répondrai à temps. Je vous remercie également de votre soutien à l'adresse du site Web de l'enseignement à cri.
Déclaration : le contenu de cet article est extrait du réseau, propriété de l'auteur original, contribué et téléchargé par les utilisateurs d'Internet, ce site ne détient pas de droits de propriété, n'a pas été édité par l'homme, et n'assume aucune responsabilité juridique. Si vous trouvez du contenu suspect de violation de droits d'auteur, veuillez envoyer un e-mail à : notice#oldtoolbag.com (veuillez remplacer # par @ lors de l'envoi d'un e-mail pour signaler une violation, et fournir des preuves pertinentes. Une fois vérifié, ce site supprimera immédiatement le contenu suspect de violation de droits d'auteur.)