English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
RecyclerView esiste da molto tempo, ma nei progetti precedenti ho sempre usato ListView, recentemente i nuovi progetti hanno usato in gran parte RecycleView. Soprattutto per il refresh in basso delle cascate, non ho trovato nulla di adatto su internet, quindi ho fatto una mia sommatoria.
Prima di tutto, vediamo alcune immagini:
Ho due metodi per implementare le funzioni di caricamento in basso e aggiornamento in basso con RecyclerView:
1. Utilizzare il SwipeRefreshLayout di Android.support.v4.widget integrato nel sistema.
2. Un componente personalizzato che contiene RecyleView.
Aggiungere un header in RecycleView non è molto semplice, prima quando usavo ListView potevo aggiungere header e footer, ma RecycleView sembra non essere così facile da gestire. Per il primo metodo che utilizza il SwipeRefreshLayout di Android.support.v4.widget, è anche molto utile, ma i prodotti generalmente non amano questo tipo di aggiornamento con scorrimento in basso, per sembrare più forti, spesso creano un loro con animazioni, il che è piuttosto ridicolo... quindi devo usare il metodo 2.
In estrema sintesi, il metodo 2 di implementazione: il layout genitore è.ViewGroup, all'interno aggiungi View, il primo è il componente header, il secondo è RecycleView, mentre il tentativo di caricamento in basso attraverso l'Adapter di RecycleView.
Con la idea in mente, mettiamoci all'opera:
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; /** * Creato da dafuShao il 9/9/2016 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); } //Inizia l'animazione public void startAnim(){ animationDrawable.start(); } //Ferma l'animazione public void stopAnim(){ animationDrawable.stop(); } }
Questo è un componente di testa molto semplice, contiene un piccolo occhio di uccello e un piccolo effetto di schiacciamento a destra e a sinistra, non allego l'immagine.
Ecco il codice del componente personalizzato che contiene 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; // The text display of footview AdapterWrapper mAdapter; boolean mIsTop = true; // Whether to slide to the top RecyclerView.LayoutManager mLayoutManager; int mLastVisibleItem; int mFirstVisibleItem; OnRecyclerStatusChangeListener mRecyclerChangeListener; int mStatus; // Current status int mHeadviewHeight; // The height of headview Scroller mScroller; int mFirstScollerY; // The initial getScrollY boolean mHasFooter; // Whether to have the function of pull-up loading boolean mShowFootVisible; // Whether to display FOOTER view when mHasFooter is true boolean mHasRefresh = true; // Whether to support pull-to-refresh private final int DEFAULT_MIN_PAGEINDEX = 1; // Default minimum page number int mMaxPage = DEFAULT_MIN_PAGEINDEX; // Total number of pages for pagination int mCurrentPage = DEFAULT_MIN_PAGEINDEX; // Current page number, starting from 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's constants private final int DELAY_LOAD_COMPLETE = 1000, DELAY_REFRESH_COMPLETE = 1000; // Tempo di recupero differito per il completamento del caricamento e dell'aggiornamento 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); } /** * Imposta il numero massimo di pagine * * @param maxPage */ public void setMaxPage(int maxPage) { this.mMaxPage = maxPage; } /** * Se supporta il caricamento in basso * * @param hasLoadmore */ public void setLoadmore(boolean hasLoadmore) { mHasFooter = hasLoadmore; } public void setRecyclerViewLayoutManage(RecyclerView.LayoutManager mLayoutManage){ this.mLayoutManager=mLayoutManage; } /** * Chiudi la funzione di aggiornamento scorrevole */ 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(); } /** * Ottiene il RecyclerView all'interno del viewgroup * * @return */ public RecyclerView getRecyclerView() { return mRecyclerView; } public void setOnRecyclerChangeListener(OnRecyclerStatusChangeListener listener) { mRecyclerChangeListener = listener; } /** * Imposta l'animazione per l'aggiunta e la rimozione degli item nel RecyclerView * * @param animator */ public void setItemAnimator(RecyclerView.ItemAnimator animator) { mRecyclerView.setItemAnimator(animator); } public void notifyDataSetChanged() { mStatus = STATUS_NORMAL; // Riesegui set dati rappresentando che il carico in più è terminato, al momento tornare allo stato normale mAdapter.notifyDataSetChanged(); } public void notifyDataInsert(int positionStart, int itemCount) { mStatus = STATUS_NORMAL; // Riesegui set dati rappresentando che il carico in più è terminato, al momento tornare allo stato normale mAdapter.notifyItemRangeInserted(positionStart, itemCount); } public void notifyDataRemove(int position) { mStatus = STATUS_NORMAL; // Riesegui set dati rappresentando che il carico in più è terminato, al momento tornare allo stato normale mAdapter.notifyItemRemoved(position); } /** * Operazione di inizializzazione * * @param context */ void init(Context context) { this.context = context; mScroller = new Scroller(context); // if (mLayoutManager!=null){ // addChildView(context,mLayoutManager); // }else{ // addChildView(context, new LinearLayoutManager(context)); // } addChildView(context); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } } /** * 增加子View * * @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); // Impostare l'animazione predefinita per l'aggiunta e la rimozione degli item 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); // Impostare l'animazione predefinita per l'aggiunta e la rimozione degli item mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mRecyclerView.addOnScrollListener(onScrollListener); mRecyclerView.setHasFixedSize(true); mRecyclerView.setLayoutParams(params); } /** * 屏蔽Recyclerview的触摸事件(下拉刷新的时候) */ 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); //Valore assoluto della differenza 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); } /** * Fai scorrere questa vista alla posizione di scorrimento della mano * * @param offsetY Valore di offset Y */ void scrollToOffset(float offsetY) { //Se stai aggiornando e lo scrolly attuale è uguale al valore iniziale, significa che sei pronto per iniziare il riavvio della discesa e eseguire una sola volta if (getScrollY() == mFristScollerY && mRecyclerChangeListener != null) mRecyclerChangeListener.startRefresh(); int value = Math.round(offsetY / 2.0F); value = mFristScollerY - value; scrollTo(0, value); } /** * Esegui l'operazione di aggiornamento, muoviti alla posizione dell'intestazione appena comparsa */ 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(); } }; /** * header返回原处完全隐藏 */ public void complete() { mCurrentPage = DEFAULT_MIN_PAGEINDEX;//完成之后当前的page恢复默认值 if (mFootViewTips != null) mFootViewTips.setText(context.getString(R.string.loading));//更改foot提示为正在加载中 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) { // Quando è la header, mostra al centro 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); // Spostarsi sotto la header per mostrare il recyleview mFristScollerY = getScrollY(); } /** * Evento di scorrimento di 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) { //滑动到了顶部 if (mFirstVisibleItem == 0) { mIsTop = true; } else { mIsTop = false; if (mStatus != STATUS_LOAD && mShowFootVisible && mLastVisibleItem + 1 == mAdapter.getItemCount()) { if (mCurrentPage == mMaxPage) { //当前页面是最后一页的时候 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) { // A causa della particolarità di StaggeredGridLayoutManager, potrebbe esserci più item visualizzati per ultimi, quindi qui viene presa un'array // Ottenere questo array e poi prendere il valore di position più grande dell'array è il valore di position visualizzato per ultimo int[] lastPositions = new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()]; ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(lastPositions); mLastVisibleItem = findMax(lastPositions); mFirstVisibleItem = ((StaggeredGridLayoutManager) mLayoutManager).findFirstVisibleItemPositions(lastPositions)[0]; setFootviewVisible(); } } }; void setFootviewVisible() { // Quando è stato impostato il caricamento in basso ma i elementi della prima pagina non sono sufficienti per riempire il Recyclerview, nascondere il footer if (mHasFooter && mFirstVisibleItem == 0) { /** * qui viene aggiunto un mShowFootVisible che diventa efficace quando la funzione di caricamento in basso è attivata, per controllare che il numero di elementi non sia sufficiente per riempire * recyclerview altezza in momento di nascosto footview, in caso di numero di elementi superiore all'altezza della vista */ 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); } } } }
Infine, c'è anche il codice dell'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); /** * Restituisce la view dell'item * * @return */ public abstract View getItemView(int viewType, ViewGroup parent); /** * Restituisce i dati di ogni item dell'Adapter, opzionale */ public Object getItem(int position) { return null; } /** * interfaccia di evento di clic su item * * @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; } } }
Occorre prestare attenzione a:
Se si desidera modificare il modo di layout di Reciview, fare questa modifica
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); // Impostare l'animazione predefinita per l'aggiunta e la rimozione degli item mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mRecyclerView.addOnScrollListener(onScrollListener); mRecyclerView.setHasFixedSize(true); mRecyclerView.setLayoutParams(params); }
mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); questa riga è quella da modificare, correggere correttamente, qui non è stato encapsulato, e verrà encapsulato nei prossimi giorni.
Occorre notare anche che nel giudizio dello scrolling se è l'ultima riga, se è un waterfall, è necessario prestare attenzione al modo di giudizio diverso dagli altri
if (mLayoutManager instanceof StaggeredGridLayoutManager) { // A causa della particolarità di StaggeredGridLayoutManager, potrebbe esserci più item visualizzati per ultimi, quindi qui viene presa un'array // Ottenere questo array e poi prendere il valore di position più grande dell'array è il valore di position visualizzato per ultimo int[] lastPositions = new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()]; ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(lastPositions); mLastVisibleItem = findMax(lastPositions); mFirstVisibleItem = ((StaggeredGridLayoutManager) mLayoutManager).findFirstVisibleItemPositions(lastPositions)[0]; setFootviewVisible(); }
因为StaggeredGridLayoutManager的特殊性可能导致最后显示的item存在多个,所以这里取到的是一个数组,得到这个数组后再取到数组中position值最大的那个就是最后显示的position值了再去设置最后一行加载更多的显示。
以上所述是小编给大家介绍的Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的,在此也非常感谢大家对呐喊教程网址的支持!
Dichiarazione: il contenuto di questo articolo è stato tratto da Internet, il copyright spetta ai rispettivi proprietari. Il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, il sito web non detiene i diritti di proprietà, non è stato editato manualmente e non assume alcuna responsabilità legale. Se trovi contenuti sospetti di violazione del copyright, ti preghiamo di inviare una email a: notice#oldtoolbag.com (al momento dell'invio dell'email, sostituisci # con @) per segnalare il problema e fornire prove pertinenti. Una volta verificata la veridicità, il sito web eliminerà immediatamente i contenuti sospetti di violazione del copyright.