Drag-and-Drop — um gesto interativo para arrastar um objeto visual pela tela com o dedo. O gesto consiste em três fases: agarrar (long press ou toque), mover (deslocar o dedo com o objeto) e soltar (largar na zona alvo). É usado para reordenar elementos, mover arquivos entre pastas e organizar interfaces (drag to reorder, drag to resize). No iOS é implementado através de UIDragInteraction, no Android — através de DragEvent e startDragAndDrop.
Principais pontos
Drag-and-Drop (DnD) é um método de manipulação de elementos gráficos de interface onde o usuário agarra um objeto com o dedo ou mouse, move-o para outra posição e o solta. Em interfaces móveis, o Drag-and-Drop geralmente é iniciado por um toque longo — o usuário mantém o elemento pressionado por 300–500 ms, após o qual o sistema muda a interface para o modo de arrasto, e o movimento do dedo desloca o objeto.
Historicamente, o Drag-and-Drop originou-se em interfaces desktop (Xerox Star, 1981; Macintosh, 1984) como alternativa aos comandos copiar/colar. Em plataformas móveis, o suporte completo apareceu no iOS 11 (2017) com UIDragInteraction e no Android 7.0 (2016) com Multi-Window Drag-and-Drop. Antes disso, o arrasto em aplicativos móveis era implementado por soluções personalizadas através de reconhecedores de gestos.
Conceitos-chave: sessão de arrasto (drag session), itens arrastáveis (drag items), visualização de arrasto (drag preview — representação visual do objeto), zona de destino (drop target), proposta de destino (drop proposal — ação ao soltar: mover, copiar, vincular). O sistema gerencia a sessão no nível do sistema operacional, permitindo arrastar objetos entre diferentes aplicativos (por exemplo, uma foto da Galeria para o Notas).
Drag-and-Drop é dividido em três fases sequenciais. Fase 1 — Agarrar (Início do Arrasto): o usuário inicia o gesto (geralmente Long Press), o sistema cria uma sessão de arrasto, captura os dados do objeto e exibe uma visualização de arrasto — uma cópia semitransparente do elemento que segue o dedo. Nesta fase, o sistema rejeita outros gestos (rolagem, seleção de texto) até que o DnD seja concluído.
Fase 2 — Mover (Movimento do Arrasto): o dedo se move pela tela, a visualização de arrasto segue o ponto de toque. O sistema determina continuamente o destino sob o dedo e envia eventos de drag-enter, drag-move e drag-exit. O destino pode reagir visualmente: destacar, expandir ou mostrar uma linha de inserção. Esta fase dura de 0,5 a 10+ segundos dependendo da complexidade da tarefa.
Fase 3 — Soltar (Queda): o dedo é levantado da tela. O sistema finaliza a sessão de arrasto e chama o callback de destino. Se o destino rejeitar a queda (por exemplo, arrastar um arquivo para uma pasta sem permissões de escrita), a visualização de arrasto retorna animadamente à posição original (animação de cancelamento). Se a queda for aceita, o elemento desaparece da posição original e aparece no destino.
| Fase | Evento iOS | Evento Android | Estado visual |
|---|---|---|---|
| Agarrar | UIDragInteractionDelegate.dragSessionWillBegin | View.OnLongClickListener → startDragAndDrop | Visualização de arrasto aparece sob o dedo |
| Mover | UIDropInteractionDelegate.dragSessionDidUpdate | View.OnDragListener.onDrag (ACTION_DRAG_MOVED) | Visualização se move, destino destaca |
| Soltar | UIDropInteractionDelegate.performDrop | View.OnDragListener.onDrag (ACTION_DROP) | Animação de queda ou retorno |
Arrasto entre aplicativos é um recurso disponível em ambas as plataformas. O iOS suporta Drag-and-Drop entre aplicativos no modo Split View e Slide Over (iPad). O Android suporta arrasto entre aplicativos no modo Split Screen. Os dados são transferidos via UIPasteConfiguration (iOS) ou ClipData (Android). O desenvolvedor especifica os tipos de dados suportados (UTI para iOS, MIME para Android).
UIDragInteraction é a principal API do iOS para arrasto, adicionada no iOS 11. Para habilitar o arrasto de uma View, adicione UIDragInteraction com UIDragInteractionDelegate. O delegado define quais elementos são arrastáveis, sua aparência e comportamento. Para receber elementos, use UIDropInteraction com UIDropInteractionDelegate, que determina se a View pode aceitar uma queda e como processar os dados.
// UIDragInteraction — arrastar um elemento de uma coleção
class DragViewController: UIViewController, UIDragInteractionDelegate {
@IBOutlet var dragSourceView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let dragInteraction = UIDragInteraction(delegate: self)
dragSourceView.addInteraction(dragInteraction)
}
func dragInteraction(_ interaction: UIDragInteraction,
itemsForBeginning session: UIDragSession) -> [UIDragItem] {
guard let data = Data(contentsOf: selectedFileURL) else { return [] }
let itemProvider = NSItemProvider(object: data as NSData)
let dragItem = UIDragItem(itemProvider: itemProvider)
// Visualização de arrasto — uma cópia reduzida do elemento
dragItem.previewProvider = {
let previewView = UIImageView(image: thumbnailImage)
previewView.alpha = 0.8
return UIDragPreview(view: previewView)
}
return [dragItem]
}
}UIDropInteractionDelegate é a interface espelho para receber quedas. O método canHandle(_:) verifica se o tipo de dado é suportado. sessionDidUpdate(_:) atualiza visualmente o estado (destaque, linha de inserção). performDrop(_:) realiza o processamento final dos dados. O iOS chama automaticamente UIDropProposal com o tipo de operação: .move, .copy, ou .forbidden. O tipo de operação afeta a exibição visual (um ícone + no canto da visualização para cópia, um sinal de proibição para forbidden).
View.startDragAndDrop() é o método do Android SDK para iniciar um arrasto, disponível desde a API 24 (Android 7.0). É chamado após agarrar o elemento (geralmente em onLongClick). Parâmetros: ClipData (dados), DragShadowBuilder (sombra visual), Object myLocalState (estado local), flags. DragShadowBuilder cria um Bitmap que segue o dedo. O sistema gerencia automaticamente as animações e os eventos de toque durante o arrasto.
// Drag-and-Drop de um elemento RecyclerView com reordenação
class ReorderAdapter(private val items: MutableList<String>) :
RecyclerView.Adapter<ReorderAdapter.ViewHolder>() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.setOnLongClickListener {
// Criando ClipData para transferência de dados
val clipData = ClipData.newPlainText("item", items[position])
// DragShadowBuilder personalizado com visualização reduzida
val shadowBuilder = object : View.DragShadowBuilder(holder.itemView) {
override fun onProvideShadowMetrics(shadowSize: Point, shadowTouchPoint: Point) {
shadowSize.set(itemView.width, itemView.height / 2)
}
}
holder.itemView.startDragAndDrop(clipData, shadowBuilder, position, 0)
true
}
}
}Recebimento de queda no Android é implementado através de View.setOnDragListener(). O listener recebe eventos ACTION_DRAG_STARTED, ACTION_DRAG_ENTERED, ACTION_DRAG_LOCATION, ACTION_DRAG_EXITED, ACTION_DROP e ACTION_DRAG_ENDED. No evento ACTION_DROP, os dados são extraídos do ClipData e a ação alvo é executada. Para RecyclerView com reordenação, recomenda-se ItemTouchHelper.SimpleCallback — um mecanismo embutido que lida automaticamente com Long Press para agarrar, animação de movimento e os métodos utilitários onMove/onSwiped.
Reordenação de listas (drag to reorder) é o cenário DnD mais comum: os usuários arrastam itens da lista para alterar sua ordem. Exemplos: reorganizar contatos nos favoritos, classificar tarefas em rastreadores (Trello, Todoist), reordenar faixas em uma playlist (Spotify, Apple Music). Implementado via ItemTouchHelper no Android ou UILongPressGestureRecognizer com reordenação de células no iOS.
Gerenciadores de arquivos — Drag-and-Drop para copiar e mover arquivos entre pastas. iOS Files App, Android Files by Google, Dropbox — todos suportam DnD. Arrastar um arquivo para uma pasta o move para dentro (move), arrastar com Option/Ctrl pressionado o copia (copy). O destino destaca visualmente ao passar o cursor, mostrando os limites da pasta.
Quadros Kanban — Trello, Jira, Notion usam Drag-and-Drop para mover cartões entre colunas (To Do → In Progress → Done). Ao arrastar, o cartão se desprende da coluna de origem, segue o dedo e, ao cruzar o limite de outra coluna, insere-se com animação. O iOS 11+ suporta UIDragPreview com efeito parallax, onde a visualização se inclina ligeiramente do ponto de toque, criando profundidade.
Seções Springboard (iOS) — arrastar ícones de aplicativos na tela inicial do iOS. Um toque longo em um ícone ativa o modo “tremendo” (os ícones balançam), após o qual o usuário pode mover ícones entre páginas e criar pastas. Este é um dos padrões de UX de DnD mais complexos, pois requer o manuseio simultâneo de arrasto, animação de tremor e agrupamento em pastas.
Feedback visual é criticamente importante para Drag-and-Drop. O objeto arrastado deve mudar: encolher (iOS: escala 0.8–0.9), tornar-se semitransparente (alpha 0.7–0.8) e elevar-se sobre a interface com uma sombra (shadow offset). O destino deve reagir visualmente ao passar o cursor: destacar, expandir ou mostrar uma borda. Sem esses sinais, o usuário não consegue saber onde pode soltar o objeto.
Animação de retorno (cancelamento de arrasto) — se o usuário soltar o elemento fora da zona de queda ou sobre uma zona inválida, o objeto deve retornar animadamente à sua posição original. O iOS UIDragInteractionDelegate fornece dragInteraction(_:sessionDidMove:) para rastreamento e dragInteraction(_:session:willEndWith:) para animação de retorno. O Android View.setOnDragListener recebe ACTION_DRAG_ENDED, onde uma animação de retorno pode ser iniciada via ValueAnimator ou ObjectAnimator.
| Recomendação | iOS | Android |
|---|---|---|
| Agarrar por gesto | Long Press (padrão) | OnLongClickListener |
| Visualização de arrasto | UIDragItem.previewProvider | DragShadowBuilder |
| Feedback háptico | UIImpactFeedbackGenerator | HapticFeedbackConstants.LONG_PRESS |
| Animação de retorno | UIDragPreviewTarget | ViewPropertyAnimator |
| Proposta de queda | UIDropProposal (.move/.copy) | ACTION_DROP + flags |
| DnD entre aplicativos | iOS 11+ Split View (iPad) | Android 7.0+ Split Screen |
Acessibilidade: usuários com limitações motoras podem não conseguir realizar o gesto de arrasto. Forneça alternativas através de botões “Mover para cima/baixo” para listas e um menu de contexto “Mover para pasta” para arquivos. O iOS VoiceOver suporta a ação de rotor “Arrastar” para mover elementos. O Android TalkBack fornece ações personalizadas através de AccessibilityNodeInfo.addAction(). Nike Run Club e Apple Health usam DnD para reordenar métricas no painel — com acesso através de botões padrão.
Perguntas frequentes
O gesto padrão para iniciar Drag-and-Drop é um toque longo de 300–500 ms. O iOS usa o mecanismo embutido UIDragInteraction que converte automaticamente um toque sustentado em um agarrar. O Android requer uma chamada explícita a startDragAndDrop() no callback OnLongClickListener. Em alguns casos (por exemplo, jogos), o DnD pode começar com um toque simples sem demora.
Sim, Drag-and-Drop entre aplicativos é suportado no iOS 11+ (iPad) e Android 7.0+. No iPad, o arrasto funciona no modo Split View e Slide Over — o usuário pode pegar texto do Safari e soltá-lo no Notas. No Android, o DnD entre aplicativos funciona no Split Screen. Os dados são transferidos via UIPasteConfiguration (iOS) ou ClipData (Android) com tipos suportados especificados (UTI, MIME).
O conflito DnD e Scroll é resolvido através de um atraso de agarre. Se o usuário começar a mover o dedo antes que o tempo limite do Long Press (500 ms) expire, o gesto é interpretado como rolagem em vez de Drag-and-Drop. No iOS, o UIDragInteraction gerencia automaticamente esse conflito através de cancelsTouchesInView. No Android, o ItemTouchHelper usa um limite — se o movimento exceder o limite TouchSlop, o Long Press é cancelado em favor da rolagem.
Drag-and-Drop suporta qualquer tipo de dado: texto, imagens, arquivos, URLs, objetos personalizados. O iOS usa NSItemProvider com tipos UTI (public.text, public.image, public.file-url). O Android usa ClipData com tipos MIME (text/plain, image/png, application/octet-stream). Para dados personalizados, um ClipData.Item com Intent é criado. Para DnD entre aplicativos, os dados são empacotados em formatos universais: texto simples, HTML, RTF, UIImage/PNG.
No iOS, a animação de retorno é tratada através de UIDragPreviewTarget — a posição final para a qual a visualização retorna animadamente. No Android, use ViewPropertyAnimator com translateX/translateY para retornar o elemento à sua posição original. A duração da animação é de 200–300 ms com uma curva de easing (EaseOut). O UIKit adiciona automaticamente um UIDragPreview com efeito parallax que retorna suavemente quando o gesto é cancelado.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também