Să presupunem că un utilizator al site-ului dvs. dorește să editeze un articol din listă fără să deschidă elementul și să caute opțiuni de editare. Dacă puteți activa această funcționalitate, acesta oferă utilizatorului respectiv un experiență de utilizare bună.

Buzunar, o aplicație de marcare deținută de Mozilla, face ceva similar. Puteți partaja / arhiva / șterge articolele salvate direct din listă fără a deschide articolul. Apoi puteți să faceți clic pe butonul meniu din colțul din dreapta sus și să selectați opțiunea de editare.

Deci, în acest tutorial vom încerca să îl codificăm.

Iată ce vrem să realizăm:

Mai întâi să creăm o listă normală RecyclerView

RecyclerView este o versiune avansată și flexibilă a ListView și GridView. Este capabil să dețină cantități mari de date din listă și are performanțe mai bune decât predecesorii săi.

După cum sugerează și numele, RecyclerView „reciclează” articolele din lista noastră odată ce nu mai este vizibilă pentru derulare și le re-completează când revin la vizualizare. Deci, containerul listei trebuie să mențină doar un număr limitat de vizualizări și nu întreaga listă.

Este atât de flexibil încât noul ViewPager2 clasa, utilizată pentru a crea file care pot fi glisate, este scrisă peste RecyclerView.

Creați un POJO (Plain Old Java Object) pentru a păstra datele din listă

public class RecyclerEntity {
    private String title;
    private boolean showMenu = false;
    private int image;

    public RecyclerEntity() {
    }

    public RecyclerEntity(String title, int image, boolean showMenu) {
        this.title = title;
        this.showMenu = showMenu;
        this.image = image;
    }

    public int getImage() {
        return image;
    }

    public void setImage(int image) {
        this.image = image;
    }
    
    //... all the getters and setters
}

Observați că avem un membru showMenu aici care se va ocupa de vizibilitatea meniului pentru acel element din listă în RecyclerView.

Creați un adaptor RecyclerView

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    List<RecyclerEntity> list;
    Context context;

    public RecyclerAdapter(Context context, List<RecyclerEntity> articlesList) {
        this.list = articlesList;
        this.context = context;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;
            v= LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_list, parent, false);
            return new MyViewHolder(v);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        RecyclerEntity entity = list.get(position);
        if(holder instanceof MyViewHolder){
            ((MyViewHolder)holder).title.setText(entity.getTitle());
            ((MyViewHolder)holder).imageView.setImageDrawable(context.getResources().getDrawable(entity.getImage()));   
        }
    }

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

    public class MyViewHolder extends RecyclerView.ViewHolder {
        TextView title;
        ImageView imageView;
        ConstraintLayout container;

        public MyViewHolder(View itemView) {
            super(itemView);
            title = itemView.findViewById(R.id.title);
            imageView = itemView.findViewById(R.id.imageView);
            container = itemView.findViewById(R.id.container);
        }
    }
}

De obicei, plasăm subclasa noastră ViewHolder (MyViewHolder) în șablonul super clasă. Acest lucru ne permite să returnăm direct obiectul nostru de subclasă ViewHolder definit din metoda onCreateViewHolder (). Atunci nu trebuie să-l aruncăm din nou și din nou în metoda onBindViewHolder ().

Dar aici nu putem face asta și vom afla de ce într-un minut.

Inițializați RecyclerView în activitate

public class MainActivity extends AppCompatActivity {
    RecyclerView recyclerView;
    List<RecyclerEntity> list;
    RecyclerAdapter adapter;


    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recyclerview);
        list = new ArrayList<>();

        list.add(new RecyclerEntity("This is the best title", R.drawable.one, false));
        list.add(new RecyclerEntity("This is the second-best title", R.drawable.two, false));
		//... rest of the list items
        
        adapter = new RecyclerAdapter(this, list);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);
    }
}

Acum să începem să facem lucrurile puțin mai interesante.

Creați o resursă de aspect pentru meniu

Și inițializați-l în Recycler Adapter:

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    List<RecyclerEntity> list;
    Context context;
    private final int SHOW_MENU = 1;
    private final int HIDE_MENU = 2;

    public RecyclerAdapter(Context context, List<RecyclerEntity> articlesList) {
        this.list = articlesList;
        this.context = context;
    }

    @Override
    public int getItemViewType(int position) {
        if(list.get(position).isShowMenu()){
            return SHOW_MENU;
        }else{
            return HIDE_MENU;
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;
        if(viewType==SHOW_MENU){
            v= LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_menu, parent, false);
            return new MenuViewHolder(v);
        }else{
            v= LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_list, parent, false);
            return new MyViewHolder(v);
        }

    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        RecyclerEntity entity = list.get(position);
        if(holder instanceof MyViewHolder){
        	//... same as above
        }
        
        if(holder instanceof MenuViewHolder){
            //Menu Actions
        }

    }

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

    public class MyViewHolder extends RecyclerView.ViewHolder {
        //... same as above
    }
    //Our menu view
    public class MenuViewHolder extends RecyclerView.ViewHolder{
        public MenuViewHolder(View view){
            super(view);
        }
    }
}

Acum avem două sub-clase ViewHolder în adaptorul nostru, MyViewHolder (elementul de listă real) și MenuViewHolder. Ambele moștenesc aceeași clasă, așa că returnăm clasa părinte RecyclerView.ViewHolder din onCreateViewHolder ().

Metoda noastră getItemViewType () returnează variabila int (viewType) care indică tipul de vizualizare pe care dorim să îl afișăm în RecyclerView pentru o anumită poziție: adică MyViewHolder sau MenuViewHolder.

Această variabilă viewType este apoi utilizată de onCreateViewHolder () care returnează efectiv obiectul ViewHolder respectiv.

Adăugați funcțiile pentru a afișa / ascunde meniul în RecyclerAdapter

public void showMenu(int position) {
        for(int i=0; i<list.size(); i++){
            list.get(i).setShowMenu(false);
        }
        list.get(position).setShowMenu(true);
        notifyDataSetChanged();
    }


    public boolean isMenuShown() {
        for(int i=0; i<list.size(); i++){
            if(list.get(i).isShowMenu()){
                return true;
            }
        }
        return false;
    }

    public void closeMenu() {
        for(int i=0; i<list.size(); i++){
            list.get(i).setShowMenu(false);
        }
        notifyDataSetChanged();
    }

Rețineți că există multe modalități de a rezolva acest lucru. Dar, de dragul simplității, păstrăm o valoare booleană în POJO pentru a menține vizibilitatea meniului.

După ce ne-am schimbat lista de date, apelăm metoda notificationDataSetChanged () pentru a redesena lista.

Afișați meniul la o apăsare lungă a elementului de listă din RecyclerAdapter

@Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        RecyclerEntity entity = list.get(position);
        if(holder instanceof MyViewHolder){
            ((MyViewHolder)holder).title.setText(entity.getTitle());
            ((MyViewHolder)holder).imageView.setImageDrawable(context.getResources().getDrawable(entity.getImage()));

            ((MyViewHolder)holder).container.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    showMenu(position);
                    return true;
                }
            });
        }
        if(holder instanceof MenuViewHolder){
            //Set Menu Actions like:
            //((MenuViewHolder)holder).edit.setOnClickListener(null);
        }

    }

Din nou, setarea evenimentelor în opiniile noastre se poate face și în diferite moduri.

În exemplul nostru, avem trei acțiuni în meniul nostru. Puteți scrie logica dvs. pentru a gestiona acțiunile respective în a doua instrucțiune if așa cum se arată în comentarii.

Afișați meniul la glisare

Pentru a face acest lucru, adăugăm un asistent tactil în MainActivity.java:

public class MainActivity extends AppCompatActivity {
    RecyclerView recyclerView;
    List<RecyclerEntity> list;
    RecyclerAdapter adapter;


    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //... same as above 
         
        adapter = new RecyclerAdapter(this, list);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);

        ItemTouchHelper.SimpleCallback touchHelperCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
            private final ColorDrawable background = new ColorDrawable(getResources().getColor(R.color.background));

            @Override
            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
                return false;
            }

            @Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
                adapter.showMenu(viewHolder.getAdapterPosition());
            }

            @Override
            public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);

                View itemView = viewHolder.itemView;

                if (dX > 0) {
                    background.setBounds(itemView.getLeft(), itemView.getTop(), itemView.getLeft() + ((int) dX), itemView.getBottom());
                } else if (dX < 0) {
                    background.setBounds(itemView.getRight() + ((int) dX), itemView.getTop(), itemView.getRight(), itemView.getBottom());
                } else {
                    background.setBounds(0, 0, 0, 0);
                }

                background.draw(c);
            }
        };
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(touchHelperCallback);
        itemTouchHelper.attachToRecyclerView(recyclerView);

    }

Apelăm la funcția showMenu () din adaptorul nostru atunci când un element din listă este glisat.

Funcția onChildDraw () desenează fundalul în timp ce glisăm. În caz contrar, va apărea un fundal alb în timp ce glisați și aspectul meniului nostru va apărea cu un pop.

Ascunderea meniului

Există trei moduri de a ne ascunde meniul.

  1. Ascunderea meniului atunci când se glisează un alt rând:

Acest caz este tratat deja în metoda showMenu () din adaptorul nostru. Înainte de a afișa meniul pentru orice rând, apelăm mai întâi setShowMenu (false) pentru ca toate rândurile să ascundă meniul.

2. Ascunderea meniului atunci când butonul Înapoi este apăsat (în Activitatea noastră):

@Override
    public void onBackPressed() {
        if (adapter.isMenuShown()) {
            adapter.closeMenu();
        } else {
            super.onBackPressed();
        }
    }

3. Ascunderea meniului atunci când un utilizator derulează lista:

recyclerView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
            @Override
            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                adapter.closeMenu();
            }
        });

Deși pocket are doar o acțiune lungă de apăsare pentru a afișa meniul, în acest exemplu am adăugat glisare pentru a afișa meniul pentru funcționalitate adăugată. Puteți ascunde elementul de meniu din glisare din nou spre dreapta / stânga, dar cred că ar putea deruta utilizatorul.

Înfășurându-se

Dacă aplicația dvs. are un set de date foarte mare de afișat într-un RecyclerView, este posibil ca acest tip de UX să nu fie calea de urmat. În acest caz, ar trebui să aveți o funcție de editare în bloc.

De asemenea, dacă opțiunile dvs. de editare sunt mai mult decât ceea ce puteți regla într-un rând RecyclerView, dar doriți totuși să afișați câteva acțiuni rapide, puteți afișa o casetă de dialog Foaie de fund la apăsarea lungă a articolului și poate avea toate opțiunile de editare. Google Drive aplicația Android face exact același lucru.

Dacă doriți să implementați o simplă glisare pentru a șterge funcția, codul pentru aceasta poate fi găsit aici pe Github.

De asemenea, puteți verifica cod sursă pentru acest proiect pe Github.

Vizita 22Boxes.com pentru mai multe resurse de dezvoltare Mobile și Web.