Page curl em Android: o que funciona e o que dá trabalho
A biblioteca moritz-wundke/android-page-curl existe desde 2013 e ainda aparece em repositórios quando alguém precisa de um efeito de folheamento simples sem escrever tudo do zero. Ela é basicamente uma View customizada que desenha a animação de curva de página usando Canvas e interpoladores. Não é mágica, funciona, mas tem limites claros que todo mundo descobre na marra. O setup é direto se você estiver usando AndroidX e Kotlin moderno. Adicione a dependência no build.gradle do módulo: implementation 'com.github.moritzwe:android-page-curl:1.0.4' (ou a branch específica que mantém o fork ativo, já que o repositório original do Moritz Wundke não recebe update há anos). Se você clonar o projeto e importar como módulo, o caminho é settings.gradle apontando para a pasta do library. Eu prefiro o Maven Central quando possível, mas essa library específica exige depender do código-fonte diretamente do GitHub ou de um mirror confável.
como usar a biblioteca moritz-wundke/android-page-curl no seu projeto
Após adicionar a dependência, o uso principal acontece através da CurlScrollView. Você coloca ela no XML como qualquer ViewGroup: <com.github.moritz.pagecurl.CurlScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Depois, no código, você infla as páginas. A API mais usada expõe métodos como addPage(View) ou setPages(List<View>), dependendo da versão do fork que você pegou. Cada "página" pode ser qualquer View — uma TextView, uma ImageView, até um RecyclerView se você quiser conteúdo rolável dentro de uma página. Aqui vai algo que o README não diz: a animação de curl usa ValueAnimator por trás das cortinas. Se você tentar mudar a duração padrão de 400ms para algo muito rápido (menos de 150ms), a curva fica truncada e o efeito visual quebra porque o interpolador não tem tempo suficiente para calcular a deformação. Do mesmo jeito, se colocar algo acima de 800ms, a experiência fica lento demais para ser confortável em scroll real. 400-500ms é o sweet spot na prática.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Um problema que eu enfrentei pessoalmente: ao usar RecyclerView dentro de uma página, o recycling entrava em conflito com a lógica interna de cache de páginas da biblioteca. O resultado era páginas que "piscavam" ou trocavam de conteúdo durante o curl. A solução foi desabilitar o recycling (setRecycledViewPool(null) no adapter) e forçar a criação de todas as views das páginas upfront. Funciona bem se você tiver menos de 10-15 páginas. Se precisar de mais, aí o custo de memória sobe rápido e você tem que pensar em outra abordagem. Outro detalhe prático: a library original não lida bem com telas de alta densidade (xxhdpi e acima) sem ajustes. As sombras e a curva de deformação ficam pixeladas porque os bitmaps de textura são renderizados em resolução fixa. A correção que eu uso é sobrescrever o método onDraw() e multiplicar os coeficientes de sombra por getResources().getDisplayMetrics().density. É uma linha ou duas de código, mas faz toda diferença visual.
Se você quer configurar o ângulo inicial do curl ou a cor da sombra, os setters são setCurlAngle(float) e setShadowColor(int). Valores angulares vão de 0 a 1.0, onde 0 é folha plana e 1.0 é a página totalmente virada. O padrão 0.5 funciona na maioria dos casos. O lado negativo que ninguém menciona: a biblioteca não suporta multi-touch nativamente. Se dois dedos tocarem a tela ao mesmo tempo, o comportamento é imprevisível — às vezes o curl inicia, às vezes trava. Para apps que precisam de gesture mais refinado, o mais honesto é considerar manter o gesto de curl no dedo único e bloquear multitouch durante a animação com requestDisallowInterceptTouchEvent(true).
Também vale saber que a biblioteca não tem suporte a RTL (right-to-left) integrado. Se seu app precisa ler em árabe ou hebraico, o curl vai na direção errada por padrão. Você precisa inverter a lógica de gestos manualmente ou encontrar um fork que já trate isso. Eu não encontrei um fork maduro que resolva isso de forma limpa, então acabei implementando a inversão no meu Activity/Fragment antes de passar o gesto para a view. Para quem está começando agora, minha recomendação prática: use essa library apenas para projetos internos, protótipos ou apps com poucas páginas estáticas. Se o produto final precisa de performance, RTL, ou centenas de páginas dinâmicas, o mais sensato é partir para uma implementação própria com SurfaceView ou migrar para soluções mais recentes como o ViewPager2 combinado com custom PageTransformer. O ViewPager2 nativo do Android já oferece transições de página que, com um pouco de desenho customizado no canvas, chegam a um resultado visual comparável sem as limitações dessa biblioteca antiga.
O repositório original está em github.com/moritzwe/android-page-curl (ou mirrors ativos no momento). Antes de clonar, verifique a data do último commit — se for anterior a 2016, espere enfrentar incompatibilidades com ferramentas modernas de build e compile. Teste sempre em pelo menos dois dispositivos com densidades diferentes antes de integrar ao seu fluxo de produção.