AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 77251037
Accepted
Oleksandr Myronchuk
Oleksandr Myronchuk
Asked: 2023-10-08 02:34:23 +0800 CST2023-10-08 02:34:23 +0800 CST 2023-10-08 02:34:23 +0800 CST

Jetpack Compose: propagando cliques para camadas inferiores da IU

  • 772

Estou trabalhando na criação de uma experiência de "integração" para meu aplicativo, que é essencialmente um guia rápido sobre como usar o aplicativo. Para conseguir isso, quero que o usuário clique onde o aplicativo o instrui. Especificamente, gostaria de criar uma interface de usuário com um elemento circular onde o usuário possa clicar e um fundo cinza transparente onde o clique não seja permitido (consulte a imagem em anexo para maior clareza)

insira a descrição da imagem aqui

Meu desafio atual consiste em descobrir como propagar esse evento de clique para as camadas inferiores da UI.

Aqui está o trecho de código relevante:

        Canvas(
        modifier = Modifier
            .fillMaxSize()
            .zIndex(3f)
            .pointerInput(Unit) {
                detectTapGestures(onTap = { offset ->
                    if (!Rect(position, volume).contains(offset)) {
                        Log.d("app-debugger9","Cancel the click!")
                    }
                    else
                    {
                        Log.d("app-debugger9","Propagate click to below UI layers!")
                    }
                })
            }
    ) {
        val circlePath = Path().apply {
            addOval(Rect(position, volume))
        }
        clipPath(circlePath, clipOp = ClipOp.Difference) {
            drawRect(SolidColor(Color.Black.copy(alpha = alphaValue)))
        }
    }

Log.d("app-debugger9", "Propagar clique para as camadas inferiores da UI!") - Esta instrução Log.d indica o momento em que pretendo propagar o evento de clique para as camadas inferiores da UI. Minha pergunta é: como posso conseguir isso?

Atualmente, não importa onde eu clique, dentro ou fora do círculo, não consigo propagar esse clique para a camada inferior da interface do usuário.

O que espero é que, se clicar fora do círculo, o clique seja ignorado. Porém, se eu clicar dentro do círculo, o clique será propagado para a camada inferior da UI, o que leva à abertura de um nível (porque esse círculo está posicionado acima do botão de abertura do nível).

Nota: 1) É possível que isso seja feito sem usar 'pointerInput(Unit)', mas não tenho certeza de como fazer isso, então optei por usar 'pointerInput(Unit)' por enquanto. 2) Quando me refiro às camadas de UI 'acima' ou 'inferiores', estou usando .zIndex(). Se o índice for maior, significa 'acima', e se o índice for menor, significa 'inferior'.

kotlin
  • 1 1 respostas
  • 60 Views

1 respostas

  • Voted
  1. Best Answer
    Thracian
    2023-10-08T15:33:46+08:002023-10-08T15:33:46+08:00

    Com Modifier.clickable ou PointerEventScope.detectTapGestures você não pode propagar gestos para descendente ou pai porque eles chamam PointeEventChange.consume(), você pode verificar esta resposta sobre gestos e propagação de gestos no Jetpack Compose. Mas no seu caso parece que você está tentando propagar um evento para o qual não é possível e um design ruim na minha opinião.

    Para qualquer pessoa que queira passar um gesto para o pai e não para o irmão, você pode facilmente escrever um gesto personalizado de detecção para cima e para baixo usando awaitEachGesture.

    @Preview
    @Composable
    private fun GesturePrpoagation() {
    
        val context = LocalContext.current
    
        Box(modifier = Modifier
            .fillMaxSize()
            .border(2.dp, Color.Red)
            .pointerInput(Unit) {
    //            detectTapGestures {
    //                Toast.makeText(context, "Parent tapped", Toast.LENGTH_SHORT).show()
    //            }
                awaitEachGesture {
                    val down: PointerInputChange = awaitFirstDown()
                    val up: PointerInputChange? = waitForUpOrCancellation()
                    Toast.makeText(context, "Parent tapped", Toast.LENGTH_SHORT).show()
                }
            }
        ) {
    
            Box(modifier = Modifier
                .background(Color.Red)
                .fillMaxSize()
                .zIndex(1f)
                .pointerInput(Unit) {
    //                detectTapGestures {
    //                    Toast.makeText(context, "Box1 tapped", Toast.LENGTH_SHORT).show()
    //                }
                    awaitEachGesture {
                        val down: PointerInputChange = awaitFirstDown()
                        val up: PointerInputChange? = waitForUpOrCancellation()
                        Toast.makeText(context, "Box1 tapped", Toast.LENGTH_SHORT).show()
                    }
                }
            )
    
            Box(modifier = Modifier
                .background(Color.Black.copy(alpha = .5f))
                .fillMaxSize()
                .zIndex(2f)
                .pointerInput(Unit) {
    //                detectTapGestures {
    //                    Toast.makeText(context, "Box2 tapped", Toast.LENGTH_SHORT).show()
    //                }
                    awaitEachGesture {
                        val down: PointerInputChange = awaitFirstDown()
                        val up: PointerInputChange? = waitForUpOrCancellation()
                        Toast.makeText(context, "Box2 tapped", Toast.LENGTH_SHORT).show()
                    }
                }
            )
        }
    }
    

    Dependendo do zIndex de Box1 e Box2 você verá que o evento será acionado para aquele com zIndex maior, se ambos zero, que é zero por padrão, serão acionados para a segunda caixa e então propagados para o pai. Você pode alterar isso seletivamente consumindo qualquer um deles PointerInputChange.

    No caso dos PO, não há necessidade de uma segunda caixa. A camada transparente preta com clipe circular pode ser desenhada usando Modifier.drawWithContent e com base na posição de toque você aciona o evento de clique nesta camada ou em qualquer componente.

    Você pode ver que enquanto os gestos de toque são consumidos, a menos que você consuma, os gestos são propagados.

    @Preview
    @Composable
    private fun TouchLayerSample() {
    
        var isTouched by remember {
            mutableStateOf(false)
        }
    
        val context = LocalContext.current
    
        Column(
            modifier = Modifier.fillMaxSize()
                .pointerInput(Unit) {
                    val size = this.size
                    val center = size.center
                    detectTapGestures { offset: Offset ->
    
                        val circleCenter = Offset(
                            x = center.x.toFloat(),
                            y = size.height * .3f
                        )
    
                        // This is for measuring distance from center of circle to
                        // touch position to invoke only invoke when touch is inside circle
                        isTouched = isTouched(
                            center = circleCenter,
                            touchPosition = offset,
                            radius = 200f
                        )
    
                        if (isTouched) {
                            Toast.makeText(context, "Circle is touched", Toast.LENGTH_SHORT).show()
                        }
    
                    }
                }
                .drawWithCache {
    
                    val center = this.size.center
    
                    val circlePath = Path().apply {
                        addOval(
                            Rect(
                                center = Offset(
                                    x = center.x,
                                    y = size.height * .3f
                                ),
                                radius = 200f
                            )
                        )
                    }
                    onDrawWithContent {
                        drawContent()
                        clipPath(circlePath, clipOp = ClipOp.Difference) {
                            drawRect(SolidColor(Color.Black.copy(alpha = .8f)))
                        }
    
                    }
                }
        ) {
    
            // This can be any content that is behind transparent black layer.
    
            // If you have a clickable component here you can use isTouched with custom gesture in previous example to not consume it inside detectTapGesture, so it can be propagated to component. In that case you need to also change PointerEventPass to Initial to propagate from ancestor to descendant.
    
            Image(
                modifier = Modifier.fillMaxSize(),
                painter = painterResource(R.drawable.landscape1),
                contentDescription = null,
                contentScale = ContentScale.FillBounds
            )
        }
    }
    
    private fun isTouched(center: Offset, touchPosition: Offset, radius: Float): Boolean {
        return center.minus(touchPosition).getDistanceSquared() < radius * radius
    }
    

    insira a descrição da imagem aqui

    • 1

relate perguntas

  • Ao usar ?.get para acessar o parâmetro Kotlin gera um erro Referência não resolvida

  • Kotlin - Como processar dados em paralelo corretamente?

  • como combinar anotação e estilo no Compose?

  • Kotlin coleta SharedFlow não está coletando

  • Como usar o filtro de vários termos no Spring Data Elasticsearch?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    destaque o código em HTML usando <font color="#xxx">

    • 2 respostas
  • Marko Smith

    Por que a resolução de sobrecarga prefere std::nullptr_t a uma classe ao passar {}?

    • 1 respostas
  • Marko Smith

    Você pode usar uma lista de inicialização com chaves como argumento de modelo (padrão)?

    • 2 respostas
  • Marko Smith

    Por que as compreensões de lista criam uma função internamente?

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Marko Smith

    java.lang.NoSuchMethodError: 'void org.openqa.selenium.remote.http.ClientConfig.<init>(java.net.URI, java.time.Duration, java.time.Duratio

    • 3 respostas
  • Marko Smith

    Por que 'char -> int' é promoção, mas 'char -> short' é conversão (mas não promoção)?

    • 4 respostas
  • Marko Smith

    Por que o construtor de uma variável global não é chamado em uma biblioteca?

    • 1 respostas
  • Marko Smith

    Comportamento inconsistente de std::common_reference_with em tuplas. Qual é correto?

    • 1 respostas
  • Marko Smith

    Somente operações bit a bit para std::byte em C++ 17?

    • 1 respostas
  • Martin Hope
    fbrereto Por que a resolução de sobrecarga prefere std::nullptr_t a uma classe ao passar {}? 2023-12-21 00:31:04 +0800 CST
  • Martin Hope
    比尔盖子 Você pode usar uma lista de inicialização com chaves como argumento de modelo (padrão)? 2023-12-17 10:02:06 +0800 CST
  • Martin Hope
    Amir reza Riahi Por que as compreensões de lista criam uma função internamente? 2023-11-16 20:53:19 +0800 CST
  • Martin Hope
    Michael A formato fmt %H:%M:%S sem decimais 2023-11-11 01:13:05 +0800 CST
  • Martin Hope
    God I Hate Python std::views::filter do C++20 não filtrando a visualização corretamente 2023-08-27 18:40:35 +0800 CST
  • Martin Hope
    LiDa Cute Por que 'char -> int' é promoção, mas 'char -> short' é conversão (mas não promoção)? 2023-08-24 20:46:59 +0800 CST
  • Martin Hope
    jabaa Por que o construtor de uma variável global não é chamado em uma biblioteca? 2023-08-18 07:15:20 +0800 CST
  • Martin Hope
    Panagiotis Syskakis Comportamento inconsistente de std::common_reference_with em tuplas. Qual é correto? 2023-08-17 21:24:06 +0800 CST
  • Martin Hope
    Alex Guteniev Por que os compiladores perdem a vetorização aqui? 2023-08-17 18:58:07 +0800 CST
  • Martin Hope
    wimalopaan Somente operações bit a bit para std::byte em C++ 17? 2023-08-17 17:13:58 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve