* Adding progressbar activity, determinate progressbars

* indeterminate linear progress bar

* adding indeterminate progress abrs

* Adding fading animation for ciruclar progress

* adding Shimmer effect

* review changes

* shimmer width and height

* seperated circular, linear and shimmer impl as well as tokens

* adding neutral style for circular progress

* review changes -2

* moving shimmer to shimmer package

Co-authored-by: PraveenKumar Yeruva <pyeruva@microsoft.com>
This commit is contained in:
PraveenKumar yeruva 2022-12-01 13:00:18 +05:30 коммит произвёл GitHub
Родитель 7e9b2ccf06
Коммит 68c8d4f136
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 993 добавлений и 0 удалений

Просмотреть файл

@ -61,6 +61,7 @@
<activity android:name="com.microsoft.fluentuidemo.demos.V2PersonaActivity" />
<activity android:name="com.microsoft.fluentuidemo.demos.PopupMenuActivity" />
<activity android:name="com.microsoft.fluentuidemo.demos.ProgressActivity" />
<activity android:name="com.microsoft.fluentuidemo.demos.V2ProgressActivity" />
<activity android:name="com.microsoft.fluentuidemo.demos.SnackbarActivity" />
<activity android:name="com.microsoft.fluentuidemo.demos.V2SegmentedControlActivity" />
<activity android:name="com.microsoft.fluentuidemo.demos.TabLayoutActivity" />

Просмотреть файл

@ -39,6 +39,7 @@ const val PERSONA_VIEW = "PersonaView"
const val V2PERSONA = "V2 Persona"
const val POPUP_MENU = "PopupMenu"
const val PROGRESS = "Progress"
const val V2PROGRESS = "V2 Progress"
const val SNACKBAR = "Snackbar"
const val V2SEGMENTED_CONTROL = "V2 SegmentedControl"
const val TAB_LAYOUT = "TabLayout"
@ -77,6 +78,7 @@ val DEMOS = arrayListOf(
Demo(V2PERSONA, V2PersonaActivity::class),
Demo(POPUP_MENU, PopupMenuActivity::class),
Demo(PROGRESS, ProgressActivity::class),
Demo(V2PROGRESS, V2ProgressActivity::class),
Demo(SNACKBAR, SnackbarActivity::class),
Demo(V2SEGMENTED_CONTROL, V2SegmentedControlActivity::class),
Demo(TAB_LAYOUT, TabLayoutActivity::class),

Просмотреть файл

@ -0,0 +1,359 @@
package com.microsoft.fluentuidemo.demos
import android.os.Bundle
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.token.AliasTokens
import com.microsoft.fluentui.theme.token.FluentStyle
import com.microsoft.fluentui.theme.token.controlTokens.CircularProgressIndicatorSize
import com.microsoft.fluentui.theme.token.controlTokens.ShimmerShape
import com.microsoft.fluentui.tokenized.progress.CircularProgressIndicator
import com.microsoft.fluentui.tokenized.progress.LinearProgressIndicator
import com.microsoft.fluentui.tokenized.progress.Shimmer
import com.microsoft.fluentuidemo.DemoActivity
import com.microsoft.fluentuidemo.R
import kotlinx.coroutines.delay
import kotlin.random.Random
class V2ProgressActivity : DemoActivity() {
override val contentLayoutId: Int
get() = R.layout.v2_activity_compose
override val contentNeedsScrollableContainer: Boolean
get() = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val compose_here = findViewById<ComposeView>(R.id.compose_here)
compose_here.setContent {
FluentTheme {
createActivityUI()
}
}
}
}
@Composable
fun createActivityUI() {
var linearProgress by remember { mutableStateOf(0f) }
var circularProgress by remember { mutableStateOf(0f) }
val textColor =
FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(
themeMode = FluentTheme.themeMode
)
val brandTextColor =
FluentTheme.aliasTokens.brandForegroundColor[AliasTokens.BrandForegroundColorTokens.BrandForeground1].value(
themeMode = FluentTheme.themeMode
)
Column(
Modifier
.padding(start = 12.dp, top = 12.dp)
.verticalScroll(rememberScrollState())
) {
LinearProgressBarExample(brandTextColor = brandTextColor, textColor = textColor)
CircularProgressBarExamples(textColor = textColor)
DeterminateProgressbarExamples(
brandTextColor = brandTextColor,
textColor = textColor,
linearProgress,
circularProgress
)
IndeterminateProgressBarExamples(brandTextColor = brandTextColor, textColor = textColor)
shimmerExamples(brandTextColor = brandTextColor, textColor = textColor)
}
LaunchedEffect(key1 = linearProgress) {
if (linearProgress >= 1.0) {
linearProgress = 1f
delay(1000)
linearProgress = 0f
} else {
delay(500)
linearProgress += Random.nextFloat() / 5
}
}
LaunchedEffect(key1 = circularProgress) {
if (circularProgress >= 1.0) {
circularProgress = 1f
delay(1000)
circularProgress = 0f
} else {
delay(500)
circularProgress += Random.nextFloat() / 5
}
}
}
@Composable
fun LinearProgressBarExample(brandTextColor: Color, textColor: Color) {
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
Text(
modifier = Modifier.padding(top = 16.dp),
text = "ProgressBars",
color = brandTextColor,
fontSize = 20.sp
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(32.dp)
) {
Text(
text = "XXXSmall - 2dp",
color = textColor
)
LinearProgressIndicator(modifier = Modifier.width(240.dp))
}
}
}
@Composable
fun CircularProgressBarExamples(textColor: Color) {
Column {
Row(
Modifier.height(42.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(32.dp)
) {
Text(
text = "XSmall - 12dp", modifier = Modifier.width(100.dp),
color = textColor
)
CircularProgressIndicator(style = FluentStyle.Brand)
CircularProgressIndicator()
}
Row(
Modifier.height(42.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(32.dp)
) {
Text(
text = "Small - 16dp", modifier = Modifier.width(100.dp),
color = textColor
)
CircularProgressIndicator(size = CircularProgressIndicatorSize.XSmall, style = FluentStyle.Brand)
CircularProgressIndicator(
CircularProgressIndicatorSize.XSmall
)
}
Row(
Modifier.height(42.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(32.dp)
) {
Text(
text = "Medium - 24dp", modifier = Modifier.width(100.dp),
color = textColor
)
CircularProgressIndicator(size = CircularProgressIndicatorSize.Medium, style = FluentStyle.Brand)
CircularProgressIndicator(
CircularProgressIndicatorSize.Medium
)
}
Row(
Modifier.height(48.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(32.dp)
) {
Text(
text = "Large - 32dp", modifier = Modifier.width(100.dp),
color = textColor
)
CircularProgressIndicator(size = CircularProgressIndicatorSize.Large, style = FluentStyle.Brand)
CircularProgressIndicator(
CircularProgressIndicatorSize.Large
)
}
Row(
Modifier.height(64.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(32.dp)
) {
Text(
text = "XLarge - 36dp", modifier = Modifier.width(100.dp),
color = textColor
)
CircularProgressIndicator(CircularProgressIndicatorSize.XLarge, style = FluentStyle.Brand)
CircularProgressIndicator(
CircularProgressIndicatorSize.XLarge
)
}
}
}
@Composable
fun DeterminateProgressbarExamples(
brandTextColor: Color,
textColor: Color,
linearProgress: Float,
circularProgress: Float
) {
Text(
modifier = Modifier.padding(top = 16.dp),
text = "Determinate ProgressBar",
color = brandTextColor,
fontSize = 18.sp
)
Text(
modifier = Modifier.padding(top = 16.dp, bottom = 8.dp),
text = "Linear Progressbar",
color = textColor
)
Row(
Modifier.height(24.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(32.dp)
) {
LinearProgressIndicator(linearProgress, modifier = Modifier.width(240.dp))
Text(text = "" + "%.0f".format(linearProgress * 100) + "%", color = textColor)
}
Text(
modifier = Modifier.padding(top = 16.dp, bottom = 16.dp),
text = "Circular ProgressBar",
color = textColor
)
Row(
Modifier.height(24.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(32.dp)
) {
CircularProgressIndicator(circularProgress, size = CircularProgressIndicatorSize.XLarge, style = FluentStyle.Brand)
Text(text = "" + "%.0f".format(circularProgress * 100) + "%", color = textColor)
}
}
@Composable
fun IndeterminateProgressBarExamples(brandTextColor: Color, textColor: Color) {
Text(
modifier = Modifier.padding(top = 16.dp),
text = "InDeterminate ProgressBar",
color = brandTextColor,
fontSize = 18.sp
)
Text(
modifier = Modifier.padding(top = 16.dp, bottom = 8.dp),
text = "Linear Progressbar",
color = textColor
)
Row(
Modifier.height(24.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(32.dp)
) {
LinearProgressIndicator(modifier = Modifier.width(240.dp))
}
Text(
modifier = Modifier.padding(top = 16.dp, bottom = 16.dp),
text = "Circular ProgressBar",
color = textColor
)
Row(
Modifier.height(24.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(32.dp)
) {
CircularProgressIndicator(size = CircularProgressIndicatorSize.XLarge, style = FluentStyle.Brand)
}
}
@Composable
fun shimmerExamples(brandTextColor: Color, textColor: Color) {
Text(
modifier = Modifier.padding(top = 16.dp),
text = "Shimmer",
color = brandTextColor,
fontSize = 18.sp
)
Text(
modifier = Modifier.padding(top = 16.dp),
text = "Box shimmer",
color = textColor
)
Row(
modifier = Modifier
.padding(top = 8.dp, bottom = 16.dp)
.height(80.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Shimmer(modifier = Modifier.size(120.dp, 80.dp))
Column(
Modifier
.height(80.dp)
.padding(top = 10.dp, bottom = 10.dp),
verticalArrangement = Arrangement.SpaceBetween
) {
Shimmer(modifier = Modifier.size(140.dp, 12.dp))
Shimmer(modifier = Modifier.size(180.dp, 12.dp))
Shimmer(modifier = Modifier.size(200.dp, 12.dp))
}
}
Text(
modifier = Modifier.padding(top = 16.dp),
text = "Circle shimmer",
color = textColor
)
Row(
modifier = Modifier
.padding(top = 8.dp, bottom = 16.dp)
.height(60.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Shimmer(modifier = Modifier.size(60.dp, 60.dp), shape = ShimmerShape.Circle)
Column(
Modifier
.height(80.dp)
.padding(top = 10.dp, bottom = 10.dp),
verticalArrangement = Arrangement.SpaceBetween
) {
Shimmer(modifier = Modifier.size(140.dp, 12.dp))
Shimmer(modifier = Modifier.size(180.dp, 12.dp))
}
}
Row(
modifier = Modifier
.padding(top = 8.dp, bottom = 16.dp)
.height(60.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Shimmer(modifier = Modifier.size(60.dp, 60.dp), shape = ShimmerShape.Circle)
Column(
Modifier
.height(80.dp)
.padding(top = 10.dp, bottom = 10.dp),
verticalArrangement = Arrangement.SpaceBetween
) {
Shimmer(modifier = Modifier.size(140.dp, 12.dp))
Shimmer(modifier = Modifier.size(180.dp, 12.dp))
}
}
Row(
modifier = Modifier
.padding(top = 8.dp, bottom = 16.dp)
.height(60.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Shimmer(modifier = Modifier.size(60.dp), shape = ShimmerShape.Circle)
Column(
Modifier
.height(80.dp)
.padding(top = 10.dp, bottom = 10.dp),
verticalArrangement = Arrangement.SpaceBetween
) {
Shimmer(modifier = Modifier.size(140.dp, 12.dp))
Shimmer(modifier = Modifier.size(180.dp, 12.dp))
}
}
}

Просмотреть файл

@ -21,15 +21,18 @@ class ControlTokens {
BottomSheet,
Button,
CheckBox,
CircularProgressIndicator,
ContextualCommandBar,
Drawer,
FloatingActionButton,
LinearProgressIndicator,
ListItem,
PillButton,
PillBar,
RadioButton,
PillSwitch,
PillTabs,
Shimmer,
ToggleSwitch
}
@ -42,15 +45,18 @@ class ControlTokens {
ControlType.BottomSheet -> BottomSheetTokens()
ControlType.Button -> ButtonTokens()
ControlType.CheckBox -> CheckBoxTokens()
ControlType.CircularProgressIndicator -> CircularProgressIndicatorTokens()
ControlType.ContextualCommandBar -> ContextualCommandBarTokens()
ControlType.Drawer -> DrawerTokens()
ControlType.FloatingActionButton -> FABTokens()
ControlType.LinearProgressIndicator -> LinearProgressIndicatorTokens()
ControlType.ListItem -> ListItemTokens()
ControlType.PillButton -> PillButtonTokens()
ControlType.PillBar -> PillBarTokens()
ControlType.RadioButton -> RadioButtonTokens()
ControlType.PillSwitch -> PillSwitchTokens()
ControlType.PillTabs -> PillTabsTokens()
ControlType.Shimmer -> ShimmerTokens()
ControlType.ToggleSwitch -> ToggleSwitchTokens()
}
}

Просмотреть файл

@ -0,0 +1,65 @@
package com.microsoft.fluentui.theme.token.controlTokens
import android.os.Parcelable
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.token.*
import kotlinx.parcelize.Parcelize
enum class CircularProgressIndicatorSize {
XXSmall,
XSmall,
Medium,
Large,
XLarge
}
data class CircularProgressIndicatorInfo(
val circularProgressIndicatorSize: CircularProgressIndicatorSize = CircularProgressIndicatorSize.XSmall,
val style: FluentStyle = FluentStyle.Neutral
) : ControlInfo
@Parcelize
open class CircularProgressIndicatorTokens : ControlToken, Parcelable {
@Composable
open fun size(circularProgressIndicatorInfo: CircularProgressIndicatorInfo): Dp {
return when (circularProgressIndicatorInfo.circularProgressIndicatorSize) {
CircularProgressIndicatorSize.XXSmall -> GlobalTokens.iconSize(GlobalTokens.IconSizeTokens.XXSmall).size
CircularProgressIndicatorSize.XSmall -> GlobalTokens.iconSize(GlobalTokens.IconSizeTokens.XSmall).size
CircularProgressIndicatorSize.Medium -> GlobalTokens.iconSize(GlobalTokens.IconSizeTokens.Medium).size
CircularProgressIndicatorSize.Large -> 32.dp
CircularProgressIndicatorSize.XLarge -> GlobalTokens.iconSize(GlobalTokens.IconSizeTokens.XLarge).size
}
}
@Composable
open fun strokeWidth(circularProgressIndicatorInfo: CircularProgressIndicatorInfo): Dp {
return when (circularProgressIndicatorInfo.circularProgressIndicatorSize) {
CircularProgressIndicatorSize.XXSmall -> GlobalTokens.strokeWidth(GlobalTokens.StrokeWidthTokens.Thinner)
CircularProgressIndicatorSize.XSmall -> GlobalTokens.strokeWidth(GlobalTokens.StrokeWidthTokens.Thinner)
CircularProgressIndicatorSize.Medium -> GlobalTokens.strokeWidth(GlobalTokens.StrokeWidthTokens.Thick)
CircularProgressIndicatorSize.Large -> 3.dp
CircularProgressIndicatorSize.XLarge -> GlobalTokens.strokeWidth(GlobalTokens.StrokeWidthTokens.Thicker)
}
}
@Composable
open fun color(circularProgressIndicatorInfo: CircularProgressIndicatorInfo): Color {
return if (circularProgressIndicatorInfo.style == FluentStyle.Neutral) {
FluentColor(
light = GlobalTokens.neutralColor(GlobalTokens.NeutralColorTokens.Grey56),
dark = GlobalTokens.neutralColor(GlobalTokens.NeutralColorTokens.Grey72)
).value(
themeMode = FluentTheme.themeMode
)
} else {
FluentTheme.aliasTokens.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackground1].value(
themeMode = FluentTheme.themeMode
)
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
package com.microsoft.fluentui.theme.token.controlTokens
import android.os.Parcelable
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.token.AliasTokens
import com.microsoft.fluentui.theme.token.ControlInfo
import com.microsoft.fluentui.theme.token.ControlToken
import com.microsoft.fluentui.theme.token.GlobalTokens
import kotlinx.parcelize.Parcelize
enum class LinearProgressIndicatorHeight{
XXXSmall
}
data class LinearProgressIndicatorInfo(
val linearProgressIndicatorHeight: LinearProgressIndicatorHeight = LinearProgressIndicatorHeight.XXXSmall,
): ControlInfo
@Parcelize
open class LinearProgressIndicatorTokens: ControlToken, Parcelable{
@Composable
open fun strokeWidth(linearProgressIndicatorInfo: LinearProgressIndicatorInfo):Dp{
return when(linearProgressIndicatorInfo.linearProgressIndicatorHeight){
LinearProgressIndicatorHeight.XXXSmall -> GlobalTokens.strokeWidth(GlobalTokens.StrokeWidthTokens.Thick)
}
}
@Composable
open fun backgroundColor(linearProgressIndicatorInfo: LinearProgressIndicatorInfo): Color {
return FluentTheme.aliasTokens.neutralStrokeColor[AliasTokens.NeutralStrokeColorTokens.Stroke1].value(
themeMode = FluentTheme.themeMode
)
}
@Composable
open fun color(linearProgressIndicatorInfo: LinearProgressIndicatorInfo): Color {
return FluentTheme.aliasTokens.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackground1].value(
themeMode = FluentTheme.themeMode
)
}
}

Просмотреть файл

@ -0,0 +1,38 @@
package com.microsoft.fluentui.theme.token.controlTokens
import android.os.Parcelable
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.token.AliasTokens
import com.microsoft.fluentui.theme.token.ControlToken
import com.microsoft.fluentui.theme.token.GlobalTokens
import kotlinx.parcelize.Parcelize
enum class ShimmerShape {
Box,
Circle
}
@Parcelize
open class ShimmerTokens : ControlToken, Parcelable {
@Composable
open fun cornerRadius(): Dp {
return GlobalTokens.borderRadius(GlobalTokens.BorderRadiusTokens.Medium)
}
@Composable
open fun knockoutEffectColor(): Color {
return FluentTheme.aliasTokens.neutralBackgroundColor[AliasTokens.NeutralBackgroundColorTokens.Stencil2].value(
themeMode = FluentTheme.themeMode
)
}
@Composable
open fun color(): Color {
return FluentTheme.aliasTokens.neutralBackgroundColor[AliasTokens.NeutralBackgroundColorTokens.Stencil1].value(
themeMode = FluentTheme.themeMode
)
}
}

Просмотреть файл

@ -30,6 +30,16 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
kotlinOptions {
jvmTarget = '1.8'
useIR = true
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion composeVersion
}
}
dependencies {
@ -40,6 +50,11 @@ dependencies {
implementation "com.google.android.material:material:$materialVersion"
androidTestImplementation "androidx.test.ext:junit:$extJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
implementation("androidx.compose.foundation:foundation:$composeVersion")
implementation("androidx.compose.material:material:$composeVersion")
implementation("androidx.compose.runtime:runtime:$composeVersion")
implementation("androidx.compose.ui:ui:$composeVersion")
}
task sourceJar(type: Jar) {

Просмотреть файл

@ -0,0 +1,177 @@
package com.microsoft.fluentui.tokenized.progress
import androidx.compose.animation.core.*
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.Stroke
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.token.ControlTokens
import com.microsoft.fluentui.theme.token.FluentStyle
import com.microsoft.fluentui.theme.token.controlTokens.CircularProgressIndicatorInfo
import com.microsoft.fluentui.theme.token.controlTokens.CircularProgressIndicatorSize
import com.microsoft.fluentui.theme.token.controlTokens.CircularProgressIndicatorTokens
import com.microsoft.fluentui.util.dpToPx
val LocalCircularProgressIndicatorTokens = compositionLocalOf { CircularProgressIndicatorTokens() }
val LocalCircularProgressIndicatorInfo = compositionLocalOf { CircularProgressIndicatorInfo() }
@Composable
fun getCircularProgressIndicatorTokens(): CircularProgressIndicatorTokens {
return LocalCircularProgressIndicatorTokens.current
}
@Composable
fun getCircularProgressIndicatorInfo(): CircularProgressIndicatorInfo {
return LocalCircularProgressIndicatorInfo.current
}
/**
* Create a Determinate Circular Progress Indicator
*
* @param progress Progress of the progress indicator. 0.0 represents no progress and 1.0 represents full progress.
* @param size Optional size of the circular progress indicator
* @param modifier Modifier for circular progress indicator
* @param style Style of progress indicator. Default: [FluentStyle.Neutral]
* @param circularProgressIndicatorTokens Token values for circular progress indicator
*
*/
@Composable
fun CircularProgressIndicator(
progress: Float,
size: CircularProgressIndicatorSize = CircularProgressIndicatorSize.XXSmall,
modifier: Modifier = Modifier,
style: FluentStyle = FluentStyle.Neutral,
circularProgressIndicatorTokens: CircularProgressIndicatorTokens? = null
) {
val tokens = circularProgressIndicatorTokens
?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.CircularProgressIndicator] as CircularProgressIndicatorTokens
CompositionLocalProvider(
LocalCircularProgressIndicatorTokens provides tokens,
LocalCircularProgressIndicatorInfo provides CircularProgressIndicatorInfo(
circularProgressIndicatorSize = size,
style = style
)
) {
val currentProgress = animateFloatAsState(
targetValue = progress.coerceIn(0f..1f),
animationSpec = tween(
delayMillis = 0,
durationMillis = 750,
easing = LinearOutSlowInEasing
)
)
val circularProgressIndicatorColor =
getCircularProgressIndicatorTokens().color(
getCircularProgressIndicatorInfo()
)
val circularProgressIndicatorSize =
getCircularProgressIndicatorTokens().size(
getCircularProgressIndicatorInfo()
)
val circularProgressIndicatorStrokeWidth =
getCircularProgressIndicatorTokens().strokeWidth(
getCircularProgressIndicatorInfo()
)
val indicatorSizeInPx = dpToPx(circularProgressIndicatorSize)
Canvas(modifier = modifier.requiredSize(circularProgressIndicatorSize)) {
drawArc(
circularProgressIndicatorColor,
-90f,
currentProgress.value * 360,
false,
size = Size(
indicatorSizeInPx,
indicatorSizeInPx
),
style = Stroke(dpToPx(circularProgressIndicatorStrokeWidth), cap = StrokeCap.Round)
)
}
}
}
/**
* Create an Indeterminate Circular Progress indicator
*
* @param size Optional size of the circular progress indicator
* @param modifier Modifier for circular progress indicator
* @param style Style of progress indicator. Default: [FluentStyle.Neutral]
* @param circularProgressIndicatorTokens Token values for circular progress indicator
*
*/
@Composable
fun CircularProgressIndicator(
size: CircularProgressIndicatorSize = CircularProgressIndicatorSize.XXSmall,
modifier: Modifier = Modifier,
style: FluentStyle = FluentStyle.Neutral,
circularProgressIndicatorTokens: CircularProgressIndicatorTokens? = null
) {
val tokens = circularProgressIndicatorTokens
?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.CircularProgressIndicator] as CircularProgressIndicatorTokens
CompositionLocalProvider(
LocalCircularProgressIndicatorTokens provides tokens,
LocalCircularProgressIndicatorInfo provides CircularProgressIndicatorInfo(
circularProgressIndicatorSize = size,
style = style
)
) {
val circularProgressIndicatorColor =
getCircularProgressIndicatorTokens().color(
getCircularProgressIndicatorInfo()
)
val circularProgressIndicatorSize =
getCircularProgressIndicatorTokens().size(
getCircularProgressIndicatorInfo()
)
val circularProgressIndicatorStrokeWidth =
getCircularProgressIndicatorTokens().strokeWidth(
getCircularProgressIndicatorInfo()
)
val infiniteTransition = rememberInfiniteTransition()
val startAngle by infiniteTransition.animateFloat(
0f,
360f,
infiniteRepeatable(
animation = tween(
durationMillis = 1000,
easing = LinearEasing
)
)
)
val indicatorSizeInPx = dpToPx(circularProgressIndicatorSize)
Canvas(
modifier = modifier
.requiredSize(circularProgressIndicatorSize)
.rotate(startAngle)
) {
drawArc(
Brush.sweepGradient(
0f to Color.Transparent,
0.6f to circularProgressIndicatorColor
),
0f,
270f,
false,
size = Size(
indicatorSizeInPx, indicatorSizeInPx
),
style = Stroke(dpToPx(circularProgressIndicatorStrokeWidth))
)
drawCircle(
color = circularProgressIndicatorColor,
radius = dpToPx(circularProgressIndicatorStrokeWidth) / 2,
center = Offset(indicatorSizeInPx / 2, 0f)
)
}
}
}

Просмотреть файл

@ -0,0 +1,188 @@
package com.microsoft.fluentui.tokenized.progress
import androidx.compose.animation.core.*
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.token.ControlTokens
import com.microsoft.fluentui.theme.token.controlTokens.LinearProgressIndicatorHeight
import com.microsoft.fluentui.theme.token.controlTokens.LinearProgressIndicatorInfo
import com.microsoft.fluentui.theme.token.controlTokens.LinearProgressIndicatorTokens
import com.microsoft.fluentui.util.dpToPx
val LocalLinearProgressIndicatorTokens = compositionLocalOf { LinearProgressIndicatorTokens() }
val LocalLinearProgressIndicatorInfo = compositionLocalOf { LinearProgressIndicatorInfo() }
@Composable
fun getLinearProgressIndicatorTokens(): LinearProgressIndicatorTokens {
return LocalLinearProgressIndicatorTokens.current
}
@Composable
fun getLinearProgressIndicatorInfo(): LinearProgressIndicatorInfo {
return LocalLinearProgressIndicatorInfo.current
}
/**
* Create a Determinate Linear Progress Indicator
*
* @param progress Progress of the progress indicator. 0.0 represents no progress and 1.0 represents full progress.
* @param linearProgressIndicatorHeight Optional width of the progress indicator
* @param modifier Modifier for linear progress indicator
* @param linearProgressIndicatorTokens Token values for linear progress indicator
*
*/
@Composable
fun LinearProgressIndicator(
progress: Float,
linearProgressIndicatorHeight: LinearProgressIndicatorHeight = LinearProgressIndicatorHeight.XXXSmall,
modifier: Modifier = Modifier,
linearProgressIndicatorTokens: LinearProgressIndicatorTokens? = null
) {
val tokens = linearProgressIndicatorTokens
?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.LinearProgressIndicator] as LinearProgressIndicatorTokens
CompositionLocalProvider(
LocalLinearProgressIndicatorTokens provides tokens,
LocalLinearProgressIndicatorInfo provides LinearProgressIndicatorInfo(
linearProgressIndicatorHeight = linearProgressIndicatorHeight
)
) {
val currentProgress = animateFloatAsState(
targetValue = progress.coerceIn(0f..1f),
animationSpec = tween(
delayMillis = 0,
durationMillis = 1000,
easing = LinearOutSlowInEasing
)
)
val linearProgressIndicatorHeight =
getLinearProgressIndicatorTokens().strokeWidth(
getLinearProgressIndicatorInfo()
)
val linearProgressIndicatorBackgroundColor =
getLinearProgressIndicatorTokens().backgroundColor(
getLinearProgressIndicatorInfo()
)
val linearProgressIndicatorColor =
getLinearProgressIndicatorTokens().color(
getLinearProgressIndicatorInfo()
)
Canvas(
modifier = modifier
.fillMaxWidth()
.requiredHeight(linearProgressIndicatorHeight)
) {
val strokeWidth = dpToPx(linearProgressIndicatorHeight)
val yOffset = strokeWidth / 2
drawLine(
linearProgressIndicatorBackgroundColor,
Offset(0f, yOffset),
Offset(size.width, yOffset),
strokeWidth
)
drawLine(
linearProgressIndicatorColor,
Offset(0f, yOffset),
Offset(currentProgress.value * (size.width), yOffset),
strokeWidth
)
}
}
}
/**
* Create an Indeterminate Linear Progress Indicator
*
* @param linearProgressIndicatorHeight Optional width of the progress indicator
* @param modifier Modifier for linear progress indicator
* @param linearProgressIndicatorTokens Token values for linear progress indicator
*
*/
@Composable
fun LinearProgressIndicator(
linearProgressIndicatorHeight: LinearProgressIndicatorHeight = LinearProgressIndicatorHeight.XXXSmall,
modifier: Modifier = Modifier,
linearProgressIndicatorTokens: LinearProgressIndicatorTokens? = null
) {
val tokens = linearProgressIndicatorTokens
?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.LinearProgressIndicator] as LinearProgressIndicatorTokens
CompositionLocalProvider(
LocalLinearProgressIndicatorTokens provides tokens,
LocalLinearProgressIndicatorInfo provides LinearProgressIndicatorInfo(
linearProgressIndicatorHeight = linearProgressIndicatorHeight
)
) {
val linearProgressIndicatorHeight =
getLinearProgressIndicatorTokens().strokeWidth(
getLinearProgressIndicatorInfo()
)
val linearProgressIndicatorBackgroundColor =
getLinearProgressIndicatorTokens().backgroundColor(
getLinearProgressIndicatorInfo()
)
val linearProgressIndicatorColor =
getLinearProgressIndicatorTokens().color(
getLinearProgressIndicatorInfo()
)
val infiniteTransition = rememberInfiniteTransition()
val animationDuration = (1750 * 0.5f).toInt()
val headAnimationDelay = 0
val tailAnimationDelay = (animationDuration * 0.5f).toInt()
val indicatorHead by infiniteTransition.animateFloat(
0f,
1f,
infiniteRepeatable(
animation = keyframes {
durationMillis = animationDuration + 500
0f at headAnimationDelay with FastOutSlowInEasing
1f at animationDuration + headAnimationDelay
}
)
)
val indicatorTail by infiniteTransition.animateFloat(
0f,
1f,
infiniteRepeatable(
animation = keyframes {
durationMillis = animationDuration + 500
0f at tailAnimationDelay with FastOutSlowInEasing
1f at animationDuration + tailAnimationDelay
}
)
)
Canvas(
modifier = modifier
.fillMaxWidth()
.requiredHeight(linearProgressIndicatorHeight)
) {
val strokeWidth = dpToPx(linearProgressIndicatorHeight)
val yOffset = strokeWidth / 2
drawLine(
linearProgressIndicatorBackgroundColor,
Offset(0f, yOffset),
Offset(size.width, yOffset),
strokeWidth
)
drawLine(
Brush.linearGradient(
0f to linearProgressIndicatorBackgroundColor,
0.5f to linearProgressIndicatorColor,
1.0f to linearProgressIndicatorBackgroundColor,
start = Offset(indicatorHead * size.width, yOffset),
end = Offset(indicatorTail * size.width, yOffset)
),
Offset(indicatorHead * size.width, yOffset),
Offset(indicatorTail * size.width, yOffset),
strokeWidth
)
}
}
}

Просмотреть файл

@ -0,0 +1,98 @@
package com.microsoft.fluentui.tokenized.progress
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.dp
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.token.ControlTokens
import com.microsoft.fluentui.theme.token.controlTokens.*
import com.microsoft.fluentui.util.dpToPx
import kotlin.math.absoluteValue
val LocalShimmerTokens = compositionLocalOf { ShimmerTokens() }
@Composable
fun getShimmerTokens(): ShimmerTokens {
return LocalShimmerTokens.current
}
/**
* Create a Shimmer effect
*
* @param shape Shape of the shimmer. See [ShimmerShape] for shapes
* @param modifier Modifier for shimmer
* @param shimmerTokens Token values for shimmer
*
*/
@Composable
fun Shimmer(
shape: ShimmerShape = ShimmerShape.Box,
modifier: Modifier = Modifier,
shimmerTokens: ShimmerTokens? = null
) {
val tokens = shimmerTokens
?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.Shimmer] as ShimmerTokens
val configuration = LocalConfiguration.current
val screenHeight = dpToPx(configuration.screenHeightDp.dp)
val screenWidth = dpToPx(configuration.screenWidthDp.dp)
val diagonal =
Math.sqrt((screenHeight * screenHeight + screenWidth * screenWidth).toDouble()).toFloat()
CompositionLocalProvider(
LocalShimmerTokens provides tokens
) {
val shimmerBackgroundColor =
getShimmerTokens().color()
val shimmerKnockoutEffectColor = getShimmerTokens().knockoutEffectColor()
val cornerRadius =
dpToPx(getShimmerTokens().cornerRadius())
val infiniteTransition = rememberInfiniteTransition()
val shimmerEffect by infiniteTransition.animateFloat(
0f,
diagonal,
infiniteRepeatable(
animation = tween(
durationMillis = 1000,
easing = LinearEasing
)
)
)
val gradientColor = Brush.linearGradient(
0f to shimmerBackgroundColor,
0.5f to shimmerKnockoutEffectColor,
1.0f to shimmerBackgroundColor,
start = Offset.Zero,
end = Offset(shimmerEffect.absoluteValue, shimmerEffect.absoluteValue)
)
if (shape == ShimmerShape.Box) {
Spacer(
modifier = modifier
.width(240.dp)
.height(12.dp)
.clip(RoundedCornerShape(cornerRadius))
.background(gradientColor)
)
} else {
Spacer(
modifier = modifier
.size(60.dp)
.clip(CircleShape)
.background(gradientColor)
)
}
}
}