Android Arsenal – Diseños

CornerCutLinearLayout extiende LinearLayout . Le permite cortar las esquinas de los padres con diferentes formas y crear una sombra adecuada para formas complejas. También le permite cortar las esquinas de cada niño.

Desarrollado por la compañía Devlight.

Además, utilizando las propiedades disponibles y los proveedores personalizados, estos cortes se pueden transformar en recortes de formas, tamaños, etc. Varios. El único propósito del widget es usarlo con niños sin transformaciones (como rotación, escala, transformaciones matriciales).

Características adicionales:

  • Soporte RTL
  • parámetros de diseño secundarios que le permiten anular los parámetros primarios predeterminados
  • sombra personalizada
  • divisores y proveedores personalizados
  • recortes y proveedores personalizados
  • proveedor de área visible con vista personalizada

Paso 1. Agregue el repositorio JitPack al archivo build.gradle de su proyecto:

  allprojects {
    repositorio {
         .. .
        maven {url   & # 39;  https://jitpack.io  & # 39;  }
    }
} 

o

  subproyectos {
    repositorio {
         .. .
        {Experto
             .. .
            url  =    " https://jitpack.io " 
        }
    }
} 

Paso 2. Agregue la siguiente dependencia al archivo build.gradle del módulo de destino:

  dependencias {
    Implementación   & # 39;  com.github.Devlight: CornerCutLinearLayout: 1.0.1  & # 39; 
} 

Para un uso rápido y simple que cubre la mayoría de los casos de uso, consulte la sección Conceptos básicos . Para una sección de uso más compleja Advanced podría ser útil.

Declaración XML

Todos los atributos del widget comienzan con el prefijo ccll_ . Los atributos de diseño para los niños comienzan con el prefijo layout_ccll_ respectivamente. Hay muchos atributos Para facilitar su uso, se separaron en algunas categorías con un prefijo inicial:

  • ccll_ o ccll_corner_cut – atributos globales del widget
  • ccll_child_ – atributos de corte secundarios globales
  • ccll_custom_shadow – atributos de sombra personalizados
  • ccll_custom_divider – atributos de divisor personalizados
  < ioout 19orn90utLout.corn. .outcorn.outcorn.outcorn.outcorn.outcorn.outcorn.outcorn.outcorn.outcorn.outcorn.outcorn.outcorn.outcorn.outcorn.outcorn.outrorn.outcorn.outcorn.outcorn.outcorn.outrorn.outcorn.outcorn.outcorn.outcorn. .outcorn.outcorn.rc.
     android :  id  =   " @ + id / ccll_kotlin_synthetic_view " 
  android   [ww=  ] " match_parent " 
  android :  layout_height  =   " wrap_content ] [19659022android:  fondo  =   " #FFFFFF " 
  android :  orientación  =   " vertical " [[[19659022]
     aplicación :  ccll_corner_cut_flag  =   " start_top | end_bottom " 
 [19459029[[65659031] [cc_90659031] 19659013] ""  "
  aplicación :  ccll_corner_cut_type  =  "  ovalado ]
     aplicación :  ccll_child_corner_cut_type  =   " oval_inverse " 

     aplicación :  ccll_custom_shadow_color  =   " # FEB545 " 
  aplicación : 19 19909031 [cc65_31]] " 16dp  "[19659074]>

    < Ver 
          Android :  layout_width  =   " match_parent " 
          Android :  layout_height  =   " 50dp " [19659014] />

    < Ver 
          Android :  layout_width  =   " match_parent " 
          Android :  layout_height  =   " 50dp " [19659014] />
</  io  .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout> 

Declaración en código (Kotlin)

Todos los atributos XML corresponden a CornerCutLinearLayout [propiedad] [19] (ccll_kotlin_synthetic_view) {
val densidad = resources.displayMetrics.density
cornerCutFlag = combine Flags ( CornerCutFlag . START_TOP CornerCutFlag . END_BOTTOM
setCornerCutSize (densidad * 24 )
setCornerCutType ( CornerCutType . OVAL )
setChildCornerCutType ( CornerCutType . OVAL_INVERSE )
customShadowColor = Color .parseColor ( " # FEB545 " )
customShadowRadius = densidad * 16
}

El resultado visual sería el siguiente:

Anatomía de corte de esquina

Por defecto CornerCutType.OVAL se usa para cortes de esquina principales y secundarios. El corte angular está limitado a su tamaño personal: profundidad y longitud .

Profundidad – en relación con el ancho de la orientación de los bordes del recorte. Longitud: en relación con la orientación de altura de los límites de recorte.

Cada una de las 4 dimensiones de los cortes de ángulo principales se puede especificar individualmente. Los cortes de esquina para niños pueden tener tamaños de lados separados ChildSideCutFlag.START y ChildSideCutFlag.END. Los cortes de las esquinas de los niños también podrían rotarse. El ángulo de rotación podría ser opcionalmente reflejado.

Como puede ver, los cortes de las esquinas de los padres están puramente delimitados en las esquinas, pero los bordes cortantes de las esquinas del niño están "reflejados". De hecho, cada corte de la esquina del niño forma un camino de espejo. Se eligió esta estrategia para que los niños pudieran ignorar por separado la parte de contacto del corte de esquina.

Además, tenga en cuenta que profundidad y longitud depende de la dirección del diseño del widget ( LinearLayout.LAYOUT_DIRECTION_LTR o LinearLayout.LAYOUT_DIRECTION_AYTAR_DIRECTION_RAYOUT_DIRECTION_AYT_DIRECTION y orientación ( LinearLayout.VERTICAL o Tipos [19659018] 1965945 Hay 5 tipos de ángulos predefinidos cortados para ángulos padre e hijo *.

  1. Óvalo.

  1. Óvalo inverso. [19659099] Rectángulo. **

  1. Rectángulo inverso. **

  1. Bisel.

* - Cada tipo de esquina del niño, de hecho, se refleja y combina en una trayectoria por los respectivos tipos de corte del ángulo del ángulo. contacto con los niños. ** - Los tipos de rectángulo admiten un radio de esquina interior. Hay atributos respectivos y propiedades de visualización para los cortes padre e hijo.

Parámetros de diseño

Cualquier hijo puede sobrescribir propiedades definido por el padre y los atribuyentes relacionado con cortes de esquina.

En los siguientes ejemplos, el padre CornerCutLinearLayout tiene ccll_child_corner_cut_type = oval_inverse y los hijos del medio tienen prioridad sobre cada esquina (19659) [19659[19659] I I .
...
aplicación : ccll_child_corner_cut_type = " oval_inverse " >
...
< Ver
...
aplicación : layout_ccll_start_top_corner_cut_type = " ovalado "
aplicación : layout_ccll_end_top_c_corn_end_top_ " bisel "

aplicación : layout_ccll_end_bottom_corner_cut_type = " rectángulo "
aplicación : layout_ccll_start_bottom_corner_cut_type = " rectángulo_inverso " />
...
</ io .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout>

Edge Child

También hay parámetros de diseño especiales para el primero y el último hijo. Por ejemplo, en una orientación vertical, cuando los elementos superiores e inferiores no están alineados con la parte superior e inferior del elemento primario respectivamente, pueden sobrescribir cortar contacto * con el elemento primario.

  < I . devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
    ...
     android :  gravedad  =   " centro " 
  aplicación :  ccll_child_corner_cut_type [194590] = [194590] " oval_inverse " >
    < Ver 
        ...
         aplicación :  layout_ccll_edge_child_parent_contact_corner_cut_type  =   " ovalado "   />
        ...
    < Ver 
        ...
         aplicación :  layout_ccll_edge_child_parent_contact_corner_cut_type  =   " rectangle_inverse "   />
</  i  .devlight. 19659119] < I  .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
    ...
     aplicación :  ccll_corner_cut_type  =   " bisel " >
    < Ver 
        ...
         aplicación :  layout_ccll_start_top_corner_cut_type  =   " ovalado " 
          aplicación :  layout_ccll_end_top_c_c_corn_end_top_ " rectángulo " 
          aplicación :  layout_ccll_edge_child_could_override_parent_corner_cut_type_if_edge_aligned  =   =  ] verdadero  verdadero 
        ...
    < Ver 
        ...
         aplicación :  layout_ccll_start_bottom_corner_cut_type  =   " rectangle_inverse " 
          aplicación :  layout_ildedgecutc_ " verdadero "   />
</  io  .devlight.xtreeivi.cornercutlinearlayout. ) Permanecen sin cambios 

Propiedades secundarias de corte de esquina secundario

Existen las siguientes propiedades adicionales de corte de esquina secundario:

  • Desplazamiento de profundidad y longitud
  • Rotación de corte de esquina

Desplazamiento de profundidad y longitud

Cada lado de los cortes angulares del niño puede tener diferentes desplazamientos de profundidad y longitud (ver anatomía arriba).

Ejemplo 1 - Desplazamiento de profundidad

  < io  .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
    ...
     aplicación :  ccll_child_corner_cut_depth_offset  =   " @ dimen / offset_24 " 
 ]    [196590]   " oval_inverse " >
    ...
    < Ver 
        ...
         aplicación :  layout_ccll_end_bottom_corner_cut_type  =   " oval " 
          app :  layout_ccll_end_top_top_corty_end_top_topty_corner_top_top_top_top_top_top_top_top_top_top_ " rectángulo_inverso " 
          aplicación :  layout_ccll_start_bottom_corner_cut_type  =   " bisel   [19452929: layout_ccll_start_top_corner_cut_type  =   " rectángulo "   />
    ...
</  io  .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout> 

Ejemplo 2 - Desplazamiento de profundidad y longitud

] ] ] ] ] ] ]  . .xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
    ...
     aplicación :  ccll_child_corner_cut_type  =   " bisel " 
  aplicación    cc  "  @ dimen / depth_offset  "
  aplicación :  ccll_child_start_side_corner_cut_length_offset  =  " ]  [ [ [ [19659031] ccll_corner_cut_type  =   " bisel " >
    < Ver 
        ...
         Android :  layout_marginTop  =   " @ dimen / offset_8 " [19659018]   Android :  layout_marginBottom  =   " @ dimen / offset_8 " [19659074] />
    < Ver 
        ...
         Android :  layout_marginTop  =   " @ dimen / offset_8 " [19659018]   Android :  layout_marginBottom  =   " @ dimen / offset_8 " [19659074] />

    < Ver 
        ...
         Android :  layout_marginTop  =   " @ dimen / offset_8 " [19659018]   Android :  layout_marginBottom  =   " @ dimen / offset_8 " [19659074] />
</  io  .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout> 

Rotación

Cada lado puede rotar los cortes de ángulo para especificar el grado. Los atributos correspondientes son:

  • ccll_child_start_side_corner_cut_rotation_degree
  • ccll_child_end_side_corner_cut_rotation_degree [angolo19659264] también deben estar en el mismo ángulo que el ángulo. Para este fin, el atributo ccll_is_child_corner_cut_end_rotation_mirrored_from_start_rotation podría ser útil.

    Cada atributo tiene la función correspondiente CornerCutLinear [1945] / 1928 / 65/90 19/65 .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
    ...
    aplicación : ccll_child_corner_cut_type = " ovalado "
    aplicación cc [19659031] " oval_inverse "
    aplicación : ccll_child_end_side_corner_cut_rotation_degree = ]
    ] [
    ] ccll_child_start_side_corner_cut_rotation_degree = " 45 " [19659074]>
    ...
    </ me .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout>

Shadow

Uno de los principales problemas de la sombra de Android es que el camino debe ser convexo.

* - Una ruta es convexa si solo tiene un contorno y curvas siempre y solo en una dirección.

Este widget le permite sortear esta limitación creando automáticamente sombras complejas (evento con recortes). Obviamente, la sombra es personalizada y tiene sus pros y sus contras.

Pro :

  • Shadow tiene propiedades personalizadas, como desplazamiento y color (ARGB).
  • Admite una ruta compleja no convexa.

Contras :

  • La sombra es artificial en comparación con la naturaleza de la sombra de la elevación nativa. Por lo tanto, no es posible confiar en la fuente global en un parámetro de posición y elevación de luz.
  • Shadow utiliza el área de vista (relleno), que debe tener en cuenta durante el proceso de diseño o el cambio dinámico del radio de la sombra.
  • La sombra NO depende del fondo de la vista o de los elementos secundarios y sus transparencias, por lo tanto, no puede ser una sombra compuesta con la superposición de diferentes niveles de transparencia (opacidad).

Por defecto, las sombras se construyen en un área principal acolchada combinada con todos los datos de recorte. Significa que la sombra NO depende del fondo de la vista, la presencia del niño o el fondo del niño. Pero este comportamiento podría ser modificado por CustomViewAreaProvider (ver sección Avanzado ).

Shadow Padding . También es posible habilitar el relleno automático de la sombra personalizada ( ccll_is_custom_shadow_auto_padding_enabled), para permitir o evitar la sombra personalizada con respecto al relleno definido por el usuario. ( ccll_could_draw_custom_shadow_over_user_defined_padding). El último atributo solo funciona junto con el primer atributo habilitado.

Ejemplos:

Divisor personalizado [19659103] El divisor personalizado tiene una anatomía y propiedades similares a aquellas el divisor LinearLayout predeterminado. Los divisores personalizados no cambian el tamaño de la vista a diferencia del divisor LinearLayout predeterminado (este último agrega espacio en la posición especificada por la bandera igual al ancho o la altura del divisor). Los divisores personalizados se ahogan en la vista y divisores predeterminados. Puede combinar el divisor predeterminado y personalizado.

Los divisores personalizados tienen varias ventajas:

  • muestran banderas adicionales
  • tapas de línea (REDONDAS, BUTT, CUADRADAS)
  • separador de línea de puntos separado
  • (ancho y espacio)
  • gravedad divisor de líneas discontinuas ( CustomDividerGravity : START CENTER END )
  • proveedor (ver sección Advanced )

Show Flags ( CustomDividerShowFlag ):

  • container_beginning - a 190000 empezando - entre el contacto del margen de la primera vista y el padre.
  • medio - entre los márgenes de contacto de los niños
  • final final - entre el contacto del margen de la primera vista y el padre. [19659006] container_end - fin de la vista

Por defecto, los divisores personalizados no se tienen en cuenta cuando se genera sombra.

Atributos del divisor personalizado:

  < io  .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
    ...
     aplicación :  ccll_custom_divider_color  =   " @ color / divisor " 
  aplicación   cc_civid   " @ dimen / divider_dash_gap  "
  aplicación  
  • : ccll_custom_divider_dash_width = @ [] [] [: ccll_custom_divider_height = " @ dimen / divider_height [19659022__line_19659031_c[19659031_cc] ] = " butt " app : ccll_custom_divider_show_flag = " aplicación : ccll_custom_divider_gravity = " centro " " ]: ccll_custom_ ding = " @ dimen / divider_padding " aplicación : ccll_custom_d ivider_padding_start [1945902020_] @ [19659013__pad] " aplicación : ccll_custom_divider_padding_end = " @ dimen / divider_padding_end> 194588] ... </ I .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout>
  • Ejemplos:

    A veces puede necesitar un área visible más compleja, divisor , recortes, etc. Para estos fines, hay proveedores personalizados para los temas antes mencionados. Todos ellos podrían especificarse mediante programación. Para mayor comodidad, también hay funciones de estilo lambda de Kotlin para la mayoría de los proveedores.

    Proveedor de corte de esquina

    CornerCutProvider le permite omitir cada una de las 4 esquinas del widget. Veamos un ejemplo.

    Ejemplo 1 .

    1. Como de costumbre, defina nuestra vista en xml (este escenario) o créelo y configúrelo mediante programación.
      < io  .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
        ...
         android :  id  =   " @ + id / ccll_corner_cut_provider " 
      app :    [19659031] ccll_eper =   " @ dimen / corner_cut_depth " 
      app  
  • :: 19659031] ccll_corner_cut_length = " aplicación : ccll_corner_cut_type = " bisel " />
    1. Establecer un [19459378] CornerCut00 19659379] ccll_corner_Cider_cutcut_cutcutter_cutcut_cutcut_cutider_cutcut_cut_cutider , corte, corte de esquina, rectF - >
      cuando (corte de esquina) {
      CornerCutFlag . START_TOP - > {
      rectF.inset (recuadro, recuadro) // recuadro - propiedad definida globalmente
      cutout.moveTo (rectF.left, rectF.top)
      cutout.lineTo (rectF.right, rectF.top)
      cutout.lineTo (rectF.left, rectF.bottom)
      verdadero // acepta la esquina superior izquierda
      }

      CornerCutFlag . END_BOTTOM - > {
      // ruta pacífica compleja
      ...
      verdadero // acepta la esquina inferior derecha
      }

      más - > falso // omita el resto de las esquinas y trátelas según la configuración predeterminada
      }
      }

    Aquí, simplemente reemplazamos los cortes de esquina superior izquierdo e inferior derecho con cortes personalizados. Se aceptan devolviendo verdadero como la última instrucción; de lo contrario, el ángulo respectivo se gestionará mediante la configuración predeterminada (si corresponde).

    Cuando necesite componer recorte con las rutas de recorte anidadas, use la función Path.addPath () (con varios fillType ) o ] Path.op () con varios Path.Op .

    El resultado sería el siguiente:

    Ejemplo 2 . En algunos escenarios, puede ser necesario transformar el trazado de recorte (cambiar el tamaño, rotar, inclinar, etc.). También existe la función opcional getTransformationMatrix () de la interfaz CornerCutProvider para este propósito. En este ejemplo, hay 3 vistas simples con un color de fondo diferente en CornerCutLinearLayout con CornerCutProvider.

    Además, como puede ver en la imagen a continuación, puede obtener efectos animados llamando a la función pública invalidateCornerCutPath () cada vez que necesite actualizar la vista después de tener cambió los valores relativos del recorte personalizado.

    Las vistas anteriores se hacen visibles a través del recorte. Además, la sombra también se dibuja correctamente alrededor del recorte. Y este recorte supera el recomendado por los límites de rectF . Para hacer esto, mejor use CustomCutoutProvider .

      ccll_corner_cut_example_2.setCornerCutProvider (
                {ver, _, _, _  - > 
                      val  matriz  =    Matriz  ()
                     val  pb  =  view.paddedBounds
                    matrix.postRotate (currentRotationAngle, pb.centerX (), pb.centerY ())
                    matriz   //  devuelve una matriz que se aplicará a la ruta de recorte. Nulo por defecto 
                },
                
                {view, clipping, cutCorner, rectF  - > 
                      when  (cutCorner) {
                         CornerCutLinearLayout .  CornerCutFlag .  START_TOP    - >  {
                             con  (view.paddedBounds) {
                                cutout.addRect (
                                    centerX ()  -  rectF.width ()  /    2.0F ,
                                    centerY ()  -  rectF.height ()  /    2.0F ,
                                    centerX ()  +  rectF.width ()  /    2.0F ,
                                    centerY ()  +  rectF.height ()  /    2.0F ,
                                     ruta .  Gestión .  CW 
                                )
                            }
                             cierto 
                        }
    
                         el resto    - >    falso 
                    }
                }
            ) 

    Proveedor de corte de esquina infantil

    ChildCornerCutProvider es casi igual a CornerCutProvider . En lugar de cutCorner tiene cutEdge y también es posible contactar a los niños - relatedCutTopChild & relatedCutBottomChild . 19659119] < I .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
    ...
    android : id = " @ + id / ccll_child_cut_provider_example_1 "
    app

  • cc = " inicio "
    aplicación : ccll_corner_cut_flag = " ninguno ] >
    ...
    </ I .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout>
  •   ccll_child_cut_provider_example_1.setChildCornerCutProvider {vista, recorte, _, rectF, _, 1980 1945] [1945][1945] [1945][1945 [19
            moveTo (rectF.centerX (), rectF.top)
            arcTo (...)
            lineTo (rectF.centerX ()  +  rectF.width (), rectF.bottom)
            arcTo (...)
            lineTo (rectF.centerX (), rectF.top)
             val  halfChordWidth  =  rectF.height ()  /    2.0F 
            AddCircle (...)
            moveTo (rectF.centerX ()  +  rectF.width (), rectF.top)
            lineTo (view.paddedBounds.right  -  rectF.width ()  /    2.0F  rectF.centerY ())
            lineTo (rectF.centerX ()  +  rectF.width (), rectF.bottom)
            lineTo (rectF.centerX ()  +  rectF.width (), rectF.top)
        }
         verdadero    //  acepta recortes personalizados 
    } 

    El resultado sería el siguiente:

    Tenga en cuenta que en este ejemplo solo los cortes en la esquina izquierda se crean con el proveedor de recortes personalizados. Esto se debe a que los proveedores solo se llaman para cutSide y cutCorner especificado por ccll_child_side_cut_flag y ccll_corner_cut] 1965 65 Ejemplo 65

      < io  .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
        ...
         android :  id  =   " @ + id / ccll_child_cut_provider_example_2 " 
     ]  cc  cc  =   " rectángulo_inverso " 
      aplicación :  ccll_corner_cut_flag  =   "] 
    ] 
    
        ...
    </  io  .devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout> 
      ccll_showcase_custom_child_cut_provider_mixed.setChildCornerCutProvider (
        {_, _, cutSide, rectF, _, _  - > 
              val  matriz  =    Matriz  ()
             cuando  (cutSide) {
                 CornerCutLinearLayout .  ChildSideCutFlag .  INICIO    - >  {
                    matrix.postSkew ( -   0.25F   0.0F  rectF.centerX (), rectF.centerY ())
                }
                 CornerCutLinearLayout .  ChildSideCutFlag .  FIN    - >  {
                    matrix.postRotate ( -   10.0F  rectF.centerX (), rectF.centerY ())
                    matrix.postTranslate ( -  rectF.width ()  /   2.0F  0.0F )
                }
            }
            matrice  //  restituisce la matrice di trasformazione che verrà applicata su ritagli precedentemente definiti 
        },
        
        {view, ritaglio, cutSide, rectF, relativeCutTopChild, _  - > 
             quando  (cutSide) {
                 CornerCutLinearLayout .  ChildSideCutFlag .  START   - >  {
                     if  (view.indexOfChild (relativeCutTopChild ?:   return  @setChildCornerCutProvider   false ) ] ] ] ritorno  @setChildCornerCutProvider   falsa 
                     //  percorso stella ritaglio 
                     true   //  accetta percorso per solo 2 (indice 1) ritaglio lato iniziale 
                }
                 CornerCutLinearLayout .  ChildSideCutFlag .  END   - >  {
                     if  (view.indexOfChild (relativeCutTopChild ?:   return  @setChildCornerCutProvider   false ) ] ] ] ritorno  @setChildCornerCutProvider   falsa 
                     //  percorso della stella del ritaglio 
                     true   //  accetta il percorso per solo 1 (indice 0) ritaglio del lato finale 
                }
                 il resto   - >   falsa 
            }
        }
    ) 

    Il risultato sarebbe il seguente:

    Custom Cutout Provider

    Questo tipo di provider ( CustomCutoutProvider ) è simile ai precedenti provider di taglio. L&#39;unica differenza è che potresti aggiungere molti provider di ritagli e il parametro rectF in entrambe le funzioni dell&#39;interfaccia restituisce i limiti imbottiti della vista.

    Provider area di visualizzazione personalizzata

    CustomViewAreaProvider utile nel caso in cui sia necessario mostrare un&#39;area di visualizzazione personalizzata. Le possibilità sono limitate solo dalla tua immaginazione e dall&#39;hardware Android. May require little knowledge of path composition, path op modes, fill types, etc.

    Example 1.

    <io.devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
        ...
        android:id="@+id/ccll_custom_view_area_provider_example_1"
        app:ccll_child_side_cut_flag="none"
        app:ccll_corner_cut_flag="none"
        app:ccll_custom_shadow_color="@color/shadow_color"
        app:ccll_custom_shadow_radius="@dimen/shadow_radius">
    
        <TextView
            ...
            android:layout_marginEnd="@dimen/offset_48"
            android:ellipsize="end"
            android:padding="@dimen/offset_16"
            android:text="Lorem ipsum dolor sit amet..." />
    </io.devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout>
    ccll_custom_view_area_provider_example_1.setCustomViewAreaProvider { view, path, rectF ->
        // properties
        val offset = view[0].marginEnd
        val cornerRadius = rectF.height() / 4.0F
        val tailCircleRadius = cornerRadius / 2.0F
        val innerTailCircleRadius = tailCircleRadius / 2.0F
        val smallCornerRadius = cornerRadius / 4.0F
        
        // left part: round rect
        path.addRoundRect(...)
        
        // right part: tail
        path.moveTo(rectF.right - offset, rectF.top + cornerRadius)
        path.arcTo(...)
        path.lineTo(rectF.right - tailCircleRadius, rectF.centerY() - innerTailCircleRadius)
        path.lineTo(rectF.right - offset + innerTailCircleRadius, rectF.centerY() - innerTailCircleRadius)
        path.arcTo(...)
        path.lineTo(rectF.right - tailCircleRadius, rectF.centerY() + innerTailCircleRadius)
        path.arcTo(...)
        path.lineTo(rectF.right - offset, rectF.top + cornerRadius)
        path.addCircle(rectF.right - tailCircleRadius, rectF.centerY(), tailCircleRadius,  Path.Direction.CW)
        path.addCircle(rectF.right - tailCircleRadius, rectF.centerY(), innerTailCircleRadius, Path.Direction.CCW)
    }

    The result would be as follow:

    Example 2.

    <io.devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
        android:id="@+id/ccll_custom_view_area_provider_example_2"
        android:gravity="center_horizontal"
        android:orientation="horizontal"
        android:padding="@dimen/offset_16"
        app:ccll_child_side_cut_flag="none"
        app:ccll_corner_cut_flag="none"
        app:ccll_could_draw_custom_shadow_over_user_defined_padding="true"
        app:ccll_custom_shadow_color="@color/accent_secondary"
        app:ccll_custom_shadow_radius="@dimen/elevation_16"
        app:ccll_is_custom_shadow_auto_padding_enabled="false">
    
        <io.devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
            ...
            android:rotation="10"
            android:rotationX="35"
            android:translationX="24dp"
            app:ccll_child_side_cut_flag="none"
            app:ccll_corner_cut_type="oval_inverse"
            app:ccll_corner_cut_size="@dimen/corner_cut_size">
            ...
        </io.devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout>
    
        <io.devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
            ...
            app:ccll_corner_cut_size="@dimen/corner_cut_size_2"
            android:layout_marginStart="@dimen/offset_16"
            android:layout_marginEnd="@dimen/offset_48"/>
    
        <View
            ...
            android:background="#8010A7E8" />
    </io.devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout>

    In this example CornerCutLinearLayout has nested CornerCutLinearLayout (first two children) and simple view (3rd child).Third child is a regular view with semi-transparent background.Second child is continuously animated (translation Y) as well. It also has its own CustomCutoutProvider in a form of a star.
    First child is transformed (constantly running animation): rotated around x, y & z sequentially.

    Also parent CustomCutoutProvider has CustomCutoutProvider set programatically (curved lines path). In this examples we want to build shadow upon only children visible area also modifying some of them (virtual corner cuts for 3rd child).

    // 1. Add Custom Cutout Provider
    val waveLineCutWidth = resources.getDimension(R.dimen.offset_12)
    val waveLineHeight = resources.getDimension(R.dimen.offset_48)
    val halfWaveLineHeight = waveLineHeight / 2.0F
    val halfWaveLineCutWidth = waveLineCutWidth / 2.0F
    ccll_custom_view_area_provider_example_2.addCustomCutoutProvider { _, cutout, rectF ->
        cutout.moveTo(rectF.left, rectF.centerY() - halfWaveLineCutWidth)
        cutout.lineTo(rectF.left + rectF.width() / 4.0FrectF.centerY() - halfWaveLineCutWidth - halfWaveLineHeight)
        cutout.lineTo(rectF.right - rectF.width() / 4.0FrectF.centerY() - halfWaveLineCutWidth + halfWaveLineHeight)
        cutout.lineTo(rectF.right, rectF.centerY() - halfWaveLineCutWidth)
        cutout.lineTo(rectF.right, rectF.centerY() + halfWaveLineCutWidth)
        cutout.lineTo(rectF.right - rectF.width() / 4.0FrectF.centerY() + halfWaveLineCutWidth + halfWaveLineHeight)
        cutout.lineTo(rectF.left + rectF.width() / 4.0FrectF.centerY() + halfWaveLineCutWidth - halfWaveLineHeight)
        cutout.lineTo(rectF.left, rectF.centerY() + halfWaveLineCutWidth)
        cutout.lineTo(rectF.left, rectF.centerY() - halfWaveLineCutWidth)
    }
    
    // 2. Set Custom View Area Provider
    ccll_showcase_custom_view_area_provider_example_2.setCustomViewAreaProvider { view, path, _ ->
        view.forEach {
            tempPath.rewind()
            if (it is CornerCutLinearLayout) {
                tempPath.offset(-it.left.toFloat(), -it.top.toFloat())
                tempPath.addPath(it.viewAreaPath) // nested ccll visible area path
                tempPath.transform(it.matrix)
                tempPath.offset(it.left.toFloat(), it.top.toFloat())
            } else {
                tempRectF.set(it.left.toFloat(), it.top.toFloat(), it.right.toFloat(), it.bottom.toFloat())
                val childCornerRadius = min(tempRectF.width(), tempRectF.height()) / 6.0F
                tempPath.addRoundRect(tempRectF, childCornerRadius, childCornerRadius, Path.Direction.CW)
                tempPath.offset(-it.left.toFloat(), -it.top.toFloat())
                tempPath.transform(it.matrix)
                tempPath.offset(it.left.toFloat(), it.top.toFloat())
            }
            path.op(tempPath, Path.Op.UNION)
        }
    }

    Note that 3rd child is clipped virtually. So when its bound overlay another visible area bounds corners of 3rd child become visible.

    As you see custom shadow are build correctly upon custom visible view area (including children cutouts and transformations) & global custom cutouts.

    The result would be as follow:

    When you nest CornerCutLinearLayout in another CornerCutLinearLayout and work with CustomViewAreaProvider it might be necessary to get current visible view area path. The copy of it could be obtained via CornerCutLinearLayout.viewAreaPath*. In similar manner widget&#39;s padded bounds could be obtained (CornerCutLinearLayout.paddedBounds).

    * - custom shadow are build upon viewAreaPath.

    Custom Divider Provider

    Sometimes you might want to have some not trivial different dividers at different positions mixed with default dividers. In a such scenario, CustomDividerProvider might come handy.

    Example 1.

    <io.devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
        android:id="@+id/ccll_custom_divider_provider_example_1"
        app:ccll_child_side_cut_flag="none"
        app:ccll_corner_cut_flag="all"
        app:ccll_corner_cut_type="oval"
        app:ccll_custom_divider_show_flag="container_beginning|middle|container_end"
        app:ccll_should_use_max_allowed_corner_cut_depth_or_length_to_be_equal="true"
        app:ccll_should_use_max_allowed_corner_cut_size="true">
        ...
    </io.devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout>

    Then we define globally divider paint:

    ccll_custom_divider_provider_example_1.doOnNonNullSizeLayout {
        val pb = it.paddedBounds
        it.customDividerProviderPaint.shader = RadialGradient(
            pb.centerX(), pb.centerY(),
            hypot(pb.width() / 2.0Fpb.height() / 2.0F) * 0.8F,
            Color.BLACKColor.WHITE,
            Shader.TileMode.CLAMP
        )
    }

    Lastly, we add CustomDividerProvider:

    ccll_custom_divider_provider_example_1.setCustomDividerProvider { _, dividerPath, dividerPaint, showDividerFlag, dividerTypeIndex, rectF ->
        when (showDividerFlag) {
            CornerCutLinearLayout.CustomDividerShowFlag.CONTAINER_BEGINNING -> {
                dividerPaint.style = Paint.Style.STROKE
                dividerPaint.strokeWidth = triangleHeight
                dividerPaint.pathEffect = PathDashPathEffect(topDividerTrianglePath, triangleBaseWidth, 0.0FPathDashPathEffect.Style.TRANSLATE)
                dividerPath.moveTo(rectF.left, rectF.top)
                dividerPath.lineTo(rectF.right, rectF.top)
            }
    
            CornerCutLinearLayout.CustomDividerShowFlag.MIDDLE -> {
                dividerPaint.style = Paint.Style.STROKE
                if (dividerTypeIndex == 0) {
                    dividerPaint.strokeWidth = circleRadius
                    dividerPaint.pathEffect = PathDashPathEffect(circleDotDividerPath, triangleBaseWidth, 0.0FPathDashPathEffect.Style.TRANSLATE)
                    dividerPath.moveTo(rectF.left, rectF.centerY())
                    dividerPath.lineTo(rectF.right + triangleBaseWidth, rectF.centerY())
                } else {
                    dividerPaint.strokeWidth = circleRadius
                    dividerPaint.pathEffect = PathDashPathEffect(diamondDotDividerPath, triangleBaseWidth, 0.0FPathDashPathEffect.Style.TRANSLATE)
                    dividerPath.moveTo(rectF.left, rectF.centerY())
                    dividerPath.lineTo(rectF.right + triangleBaseWidth, rectF.centerY())
                }
            }
    
            CornerCutLinearLayout.CustomDividerShowFlag.CONTAINER_END -> {
                dividerPaint.style = Paint.Style.STROKE
                dividerPaint.strokeWidth = triangleHeight
                dividerPaint.pathEffect = PathDashPathEffect(bottomDividerTrianglePath, triangleBaseWidth, 0.0FPathDashPathEffect.Style.TRANSLATE)
                dividerPath.moveTo(rectF.left, rectF.top)
                dividerPath.lineTo(rectF.right, rectF.top)
            }
        }
        true // accept divider path
    }

    The result would be as follow:

    Example 2. This example shows the combination of custom and default&#39;s dividers.

    <io.devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout
        android:id="@+id/ccll_custom_divider_provider_example_2"
        app:ccll_child_corner_cut_type="oval_inverse"
        app:ccll_corner_cut_flag="all"
        app:ccll_custom_divider_color="@color/divider"
        app:ccll_custom_divider_dash_gap="@dimen/divider_dash_gap"
        app:ccll_custom_divider_dash_width="@dimen/divider_dash_width"
        app:ccll_custom_divider_height="@dimen/divider_height"
        app:ccll_custom_divider_line_cap="round"
        app:ccll_custom_divider_show_flag="middle"
        app:ccll_custom_shadow_color="@color/accent_secondary">
        ...
    </io.devlight.xtreeivi.cornercutlinearlayout.CornerCutLinearLayout>
    ccll_custom_divider_provider_example_2.setCustomDividerProvider { _, dividerPath, dividerPaint, showDividerFlag, dividerTypeIndex, rectF ->
        when (showDividerFlag) {
            CornerCutLinearLayout.CustomDividerShowFlag.MIDDLE -> {
                dividerPaint.style = Paint.Style.STROKE
                when (dividerTypeIndex) {
                    0 -> {
                        dividerPaint.shader = RadialGradient(
                            rectF.centerX(), rectF.centerY(),
                            rectF.width() / 2.0FColor.GREENColor.RED,
                            Shader.TileMode.MIRROR
                        )
                        dividerPaint.strokeWidth = circleRadius
                        dividerPaint.pathEffect = PathDashPathEffect(diamondDotDividerPath, triangleBaseWidth, 0.0FPathDashPathEffect.Style.TRANSLATE)
                        dividerPath.moveTo(rectF.left, rectF.centerY())
                        dividerPath.lineTo(rectF.right + triangleBaseWidth, rectF.centerY())
                        return@setCustomDividerProvider true // accept divider and draw it
                    }
                    
                    2 -> {
                        dividerPaint.shader = LinearGradient(
                            rectF.centerX(), rectF.centerY() - halfWaveHeight,
                            rectF.centerX(), rectF.centerY() + halfWaveHeight,
                            Color.BLUEColor.YELLOWShader.TileMode.CLAMP
                        )
                        dividerPaint.strokeWidth = halfWaveHeight * 2.0F
                        dividerPaint.pathEffect = PathDashPathEffect( wavePath, halfWaveWidth * 2.0F0.0FPathDashPathEffect.Style.TRANSLATE)
                        dividerPath.moveTo(rectF.left, rectF.centerY())
                        dividerPath.lineTo(rectF.right, rectF.centerY())
                        return@setCustomDividerProvider true // accept divider and draw it
                    }
                    
                    else -> return@setCustomDividerProvider false // skip divider and draw default
                }
            }
        }
        false // skip divider and draw default
    }

    The result would be as follow:

    Sample App

    In order to take closer look over examples provided and library in general, please run the sample app.

    Author

    Created by Mykola Melnyk - @xtreeivi

    Donation

    I would appreciate if you "toss the coin to your faithful servant". Details: 5432 5912 5316 4615 (MasterCard)

    Company

              

    Here you can see open source works developed by Devlight LLC. This and another works is an exclusive property of Devlight LLC.

    If you want to use this library in applications which will be available on Google Play, please report us or author of the library about it.

    Whether you&#39;re searching for a new partner or trusted team for creating your new great product we are always ready to start work with you.

    You can contact us: info@devlight.io or opensource@devlight.io. Thanks in advance.

    Devlight LLC, 2020 devlight.io

    License

    Please see LICENSE

    .

    Compruebe también

    en vivo desde Droidcon, incluida la mayor actualización de Gemini en Android Studio y más lanzamientos del SDK de Android.

    Acabamos de lanzar nuestro episodio de otoño de #TheAndroidShow en YouTube etcétera desarrollador.android.comy esta vez …

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *