Pergunta

Alguém pode fornecer código ou pseudocódigo sobre como os links de paginação no StackOverflow são gerados?

Fico quebrando a cabeça, mas não consigo pensar em uma maneira decente de construir links dinâmicos que sempre mostram as 2 páginas em torno da atual, mais a primeira e a última.

Exemplo: 1 ... 5 6 7 ... 593

Foi útil?

Solução

Já existem várias outras respostas, mas gostaria de mostrar a abordagem que usei para resolvê-las:Primeiro, vamos verificar como o Stack Overflow lida com casos normais e extremos.Cada uma das minhas páginas exibe 10 resultados, então para descobrir o que ela faz para 1 página, encontre uma tag que tenha menos de 11 entradas: usabilidade funciona hoje.Podemos ver que nada é exibido, o que faz sentido.

Que tal 2 páginas?Encontre uma tag que tenha entre 11 e 20 entradas (emacs funciona hoje).Nós vemos:"1 2 Próximo" ou "Anterior 1 2", dependendo da página em que estamos.

3 páginas?"1 2 3 ...3 Próximo", "Anterior 1 2 3 Próximo" e "Anterior 1 ...2 3".Curiosamente, podemos ver que o Stack Overflow em si não lida muito bem com esse caso extremo:deve exibir "1 2 ...3 Próximo"

4 páginas?"1 2 3 ...4 Próximo", "Anterior 1 2 3 ...4 Próximo", "Anterior 1 ...2 3 4 Próximo" e "Anterior 1 ...3 4"

Finalmente, vejamos o caso geral, N páginas:"1 2 3 ...N Próximo", "Anterior 1 2 3 ...N Próximo", "Anterior 1 ...2 3 4 ...N Próximo", "Anterior 1 ...3 4 5 ...N Próximo", etc.

Vamos generalizar com base no que vimos:O algoritmo parece ter estas características em comum:

  • Se não estivermos na primeira página, exiba o link para Anterior
  • Sempre exiba o número da primeira página
  • Sempre exibir o número da página atual
  • Sempre exiba a página antes desta página e a página depois desta página.
  • Sempre exibir o número da última página
  • Se não estivermos na última página, exiba o link para Próximo

Vamos ignorar o caso extremo de uma única página e fazer uma boa primeira tentativa no algoritmo:(Como foi mencionado, o código para imprimir os links seria mais complicado.Imagine cada lugar onde colocamos um número de página, Anterior ou Próximo, como uma chamada de função que retornará o URL correto.)

function printPageLinksFirstTry(num totalPages, num currentPage)
  if ( currentPage > 1 )
    print "Prev"
  print "1"
  print "..."
  print currentPage - 1
  print currentPage
  print currentPage + 1
  print "..."
  print totalPages
  if ( currentPage < totalPages )
    print "Next"
endFunction

Esta função funciona bem, mas não leva em consideração se estamos perto da primeira ou da última página.Olhando para os exemplos acima, queremos apenas exibir o ...se a página atual estiver a dois ou mais de distância.

function printPageLinksHandleCloseToEnds(num totalPages, num currentPage)
  if ( currentPage > 1 )
    print "Prev"
  print "1"
  if ( currentPage > 2 )
    print "..."
  if ( currentPage > 2 )
    print currentPage - 1
  print currentPage
  if ( currentPage < totalPages - 1 )
    print currentPage + 1
  if ( currentPage < totalPages - 1 )
    print "..."
  print totalPages
  if ( currentPage < totalPages )
    print "Next"
endFunction

Como você pode ver, temos algumas duplicações aqui.Podemos ir em frente e limpar isso para facilitar a leitura:

function printPageLinksCleanedUp(num totalPages, num currentPage)
  if ( currentPage > 1 )
    print "Prev"
  print "1"
  if ( currentPage > 2 )
    print "..."
    print currentPage - 1
  print currentPage
  if ( currentPage < totalPages - 1 )
    print currentPage + 1
    print "..."
  print totalPages
  if ( currentPage < totalPages )
    print "Next"
endFunction

Restam apenas dois problemas.Primeiro, não imprimimos corretamente uma página e, segundo, imprimiremos "1" duas vezes se estivermos na primeira ou na última página.Vamos limpar os dois de uma só vez:

function printPageLinksFinal(num totalPages, num currentPage)
  if ( totalPages == 1 )
    return

  if ( currentPage > 1 )
    print "Prev"

  print "1"

  if ( currentPage > 2 )
    print "..."
    print currentPage - 1

  if ( currentPage != 1 and currentPage != totalPages )
    print currentPage

  if ( currentPage < totalPages - 1 )
    print currentPage + 1
    print "..."

  print totalPages

  if ( currentPage < totalPages )
    print "Next"

endFunction

Na verdade, eu menti:Temos um problema restante.Quando você tem pelo menos 4 páginas e está na primeira ou na última página, você ganha uma página extra no seu display.Em vez de "1 2 ...10 Próximo "você obtém"1 2 3 ...10 Próximo".Para corresponder exatamente ao que está acontecendo no Stack Overflow, você terá que verificar esta situação:

function printPageLinksFinalReally(num totalPages, num currentPage)
  if ( totalPages == 1 )
    return

  if ( currentPage > 1 )
    print "Prev"

  print "1"

  if ( currentPage > 2 )
    print "..."
    if ( currentPage == totalPages and totalPages > 3 )
      print currentPage - 2
    print currentPage - 1

  if ( currentPage != 1 and currentPage != totalPages )
    print currentPage

  if ( currentPage < totalPages - 1 )
    print currentPage + 1
    if ( currentPage == 1 and totalPages > 3 )
      print currentPage + 2
    print "..."

  print totalPages

  if ( currentPage < totalPages )
    print "Next"

endFunction

Eu espero que isso ajude!

Outras dicas

Os controles geralmente mostram controles para:P1, Pn, Pc (página atual), Pc+1, Pc-1.A única vez que isso muda é nas extremidades do intervalo de paginação {Pc <P3 ou Pc > (Pn-3)}

  • O primeiro passo é obviamente calcular o número de páginas:

numPages = ceiling(totalRecords / numPerPage)

  • Se você tiver 4 ou menos, desista neste ponto, pois, pelas regras acima, a paginação sempre será fixa (P1, P2, Pn-1, Pn), onde um será efetivamente Pc

  • caso contrário, você terá três "estados"

a.(Pc < P3) - então mostre P1, P2, P3, Pn, Next Se Pc >1, mostre um link 'anterior' antes de P1.

b.(Pc > Pn - 2), então mostre Anterior, P1, Pn - 2, Pn -1, Pn, mostre um link Próximo se Pc < Pn

c.Mostrar Anterior, P1, Pc -1, Pc, Pc +1, Pn, Próximo

Fácil como torta em pseudocódigo.Os loops podem ficar um pouco desagradáveis ​​quando implementados, pois você precisa fazer algumas iterações para gerar os links.

Editar:É claro que Anterior e Próximo são idênticos a Pc +/- 1

Bem, se você conhece a página atual, é bastante trivial apenas subtrair o número por 1 e adicioná-lo por 1, depois comparar esses números com os limites e exibir sempre a primeira e a última página, se eles não estiverem em sequência , adicione as reticências.

Ou você está perguntando sobre como obter o número total de páginas e determinar o número da página atual...?

public void PageLinks(int currentPage, int lastPage) {
    if (currentPage > 2) 
        Add('[1]', '...');
    for(int i=Math.Max(1, currentPage-1); i< Math.Min(currentPage+1, lastPage); i++)
        Add('[i]');
    if (currentPage < lastPage-1)
        Add('...', '[lastpage]');
}

lastPage é calculado como Math.Ceiling(totalRecords/RecordsPerPage).

hmmm.Na verdade, no caso de o CurrentPage é 3, ela ainda mostra [1] ... [2] [3] [4] ... [xxx] Acho que as elipses são supérfluas nesse caso.Mas é assim que funciona.

Editar:a visualização formata o bloco de código corretamente, por que ele fica mutilado?claro, é apenas pseudocódigo....mas ainda....

Esta é a minha abordagem para criar um link de paginação.A seguir código java é apenas um pseudo.

package com.edde;

/**
 * @author Yang Shuai
 */
public class Pager {

    /**
     * This is a method used to display the paging links(pagination or sometimes called pager).
     * The totalPages are the total page you need to display. You can get this value using the
     * formula:
     * 
     *     total_pages = total_records / items_per_page
     * 
     * This methods is just a pseudo-code.
     * 
     * 
     * @param totalPages how many pages you need to display
     * @param currentPage you are in which page now
     */
    public static void printPageLinks(int totalPages, int currentPage) {

        // how many pages to display before and after the current page
        int x = 2;

        // if we just have one page, show nothing
        if (totalPages == 1) {
            return;
        }

        // if we are not at the first page, show the "Prev" button
        if (currentPage > 1) {
            System.out.print("Prev");
        }

        // always display the first page
        if (currentPage == 1) {
            System.out.print("    [1]");
        } else {
            System.out.print("    1");
        }

        // besides the first and last page, how many pages do we need to display?
        int how_many_times = 2 * x + 1;

        // we use the left and right to restrict the range that we need to display
        int left = Math.max(2, currentPage - 2 * x - 1);
        int right = Math.min(totalPages - 1, currentPage + 2 * x + 1);

        // the upper range restricted by left and right are more loosely than we need,
        // so we further restrict this range we need to display
        while (right - left > 2 * x) {
            if (currentPage - left < right - currentPage) {
                right--;
                right = right < currentPage ? currentPage : right;
            } else {
                left++;
                left = left > currentPage ? currentPage : left;
            }
        }

        // do we need display the left "..."
        if (left >= 3) {
            System.out.print("    ...");
        }

        // now display the middle pages, we display how_many_times pages from page left
        for (int i = 1, out = left; i <= how_many_times; i++, out++) {
            // there are some pages we need not to display
            if (out > right) {
                continue;
            }

            // display the actual page
            if (out == currentPage) {
                System.out.print("    [" + out + "]");
            } else {
                System.out.print("    " + out);
            }
        }

        // do we need the right "..."
        if (totalPages - right >= 2) {
            System.out.print("    ...");
        }

        // always display the last page
        if (currentPage == totalPages) {
            System.out.print("    [" + totalPages + "]");
        } else {
            System.out.print("    " + totalPages);
        }

        // if we are not at the last page, then display the "Next" button
        if (currentPage < totalPages) {
            System.out.print("    Next");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        // printPageLinks(50, 3);
        help(500);
    }

    public static void test(int n) {
        for (int i = 1; i <= n; i++) {
            printPageLinks(n, i);
        }
        System.out.println("------------------------------");
    }

    public static void help(int n) {
        for (int i = 1; i <= n; i++) {
            test(i);
        }
    }

    public static void help(int from, int to) {
        for (int i = from; i <= to; i++) {
            test(i);
        }
    }

}

Aqui está o meu algoritmo, ele funciona muito bem:

    // Input
    total_items   // Number of rows, records etc. from db, file or whatever
    per_page      // num items per page
    page          // current page
    visible_pages // number of visible pages

    // Calculations
    lastPage = ceil(total_items / per_page);
    prevPage = page - 1 < 1 ? 0 : page - 1;
    nextPage = page + 1 > lastPage ? 0 : page + 1;
    halfpages = ceil(visible_pages / 2);
    startPage = page - halfpages < 1 ? 1 : page - halfpages;
    endPage = startPage + visible_pages - 1;
    if(endPage > lastPage) {
        startPage -= endPage - lastPage;
        startPage = startPage < 1 ? 1 : startPage;
        endPage = startPage + visible_pages > lastPage ? lastPage : startPage + visible_pages - 1;
    }

    // Output
    lastPage    // Total number of pages
    prevPage    // Previous page number (if 0 there is no prev page)
    nextPage    // Next page number (if 0 there is no next page)
    startPage   // First visible page
    endPage     // Last visible page

Então você pode fazer um pager assim:

    if prevPage
    [1] [prevPage] 
    endif

    [startPage] ... [endPage] 

    if nextPage
    [nextPage] [lastPage] 
    endif

ou personalize o que quiser.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top