mobile-android-design

安装量: 6.4K
排名: #532

安装

npx skills add https://github.com/wshobson/agents --skill mobile-android-design

Android Mobile Design

Master Material Design 3 (Material You) and Jetpack Compose to build modern, adaptive Android applications that integrate seamlessly with the Android ecosystem.

When to Use This Skill Designing Android app interfaces following Material Design 3 Building Jetpack Compose UI and layouts Implementing Android navigation patterns (Navigation Compose) Creating adaptive layouts for phones, tablets, and foldables Using Material 3 theming with dynamic colors Building accessible Android interfaces Implementing Android-specific gestures and interactions Designing for different screen configurations Core Concepts 1. Material Design 3 Principles

Personalization: Dynamic color adapts UI to user's wallpaper Accessibility: Tonal palettes ensure sufficient color contrast Large Screens: Responsive layouts for tablets and foldables

Material Components:

Cards, Buttons, FABs, Chips Navigation (rail, drawer, bottom nav) Text fields, Dialogs, Sheets Lists, Menus, Progress indicators 2. Jetpack Compose Layout System

Column and Row:

// Vertical arrangement with alignment Column( modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp), horizontalAlignment = Alignment.Start ) { Text( text = "Title", style = MaterialTheme.typography.headlineSmall ) Text( text = "Subtitle", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) }

// Horizontal arrangement with weight Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Icon(Icons.Default.Star, contentDescription = null) Text("Featured") Spacer(modifier = Modifier.weight(1f)) TextButton(onClick = {}) { Text("View All") } }

Lazy Lists and Grids:

// Lazy column with sticky headers LazyColumn { items.groupBy { it.category }.forEach { (category, categoryItems) -> stickyHeader { Text( text = category, modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.surface) .padding(16.dp), style = MaterialTheme.typography.titleMedium ) } items(categoryItems) { item -> ItemRow(item = item) } } }

// Adaptive grid LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 150.dp), contentPadding = PaddingValues(16.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { items(items) { item -> ItemCard(item = item) } }

  1. Navigation Patterns

Bottom Navigation:

@Composable fun MainScreen() { val navController = rememberNavController()

Scaffold(
    bottomBar = {
        NavigationBar {
            val navBackStackEntry by navController.currentBackStackEntryAsState()
            val currentDestination = navBackStackEntry?.destination

            NavigationDestination.entries.forEach { destination ->
                NavigationBarItem(
                    icon = { Icon(destination.icon, contentDescription = null) },
                    label = { Text(destination.label) },
                    selected = currentDestination?.hierarchy?.any {
                        it.route == destination.route
                    } == true,
                    onClick = {
                        navController.navigate(destination.route) {
                            popUpTo(navController.graph.findStartDestination().id) {
                                saveState = true
                            }
                            launchSingleTop = true
                            restoreState = true
                        }
                    }
                )
            }
        }
    }
) { innerPadding ->
    NavHost(
        navController = navController,
        startDestination = NavigationDestination.Home.route,
        modifier = Modifier.padding(innerPadding)
    ) {
        composable(NavigationDestination.Home.route) { HomeScreen() }
        composable(NavigationDestination.Search.route) { SearchScreen() }
        composable(NavigationDestination.Profile.route) { ProfileScreen() }
    }
}

}

Navigation Drawer:

@Composable fun DrawerNavigation() { val drawerState = rememberDrawerState(DrawerValue.Closed) val scope = rememberCoroutineScope()

ModalNavigationDrawer(
    drawerState = drawerState,
    drawerContent = {
        ModalDrawerSheet {
            Spacer(Modifier.height(12.dp))
            Text(
                "App Name",
                modifier = Modifier.padding(16.dp),
                style = MaterialTheme.typography.titleLarge
            )
            HorizontalDivider()

            NavigationDrawerItem(
                icon = { Icon(Icons.Default.Home, null) },
                label = { Text("Home") },
                selected = true,
                onClick = { scope.launch { drawerState.close() } }
            )
            NavigationDrawerItem(
                icon = { Icon(Icons.Default.Settings, null) },
                label = { Text("Settings") },
                selected = false,
                onClick = { }
            )
        }
    }
) {
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Home") },
                navigationIcon = {
                    IconButton(onClick = { scope.launch { drawerState.open() } }) {
                        Icon(Icons.Default.Menu, contentDescription = "Menu")
                    }
                }
            )
        }
    ) { innerPadding ->
        Content(modifier = Modifier.padding(innerPadding))
    }
}

}

  1. Material 3 Theming

Color Scheme:

// Dynamic color (Android 12+) val dynamicColorScheme = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } else { if (darkTheme) DarkColorScheme else LightColorScheme }

// Custom color scheme private val LightColorScheme = lightColorScheme( primary = Color(0xFF6750A4), onPrimary = Color.White, primaryContainer = Color(0xFFEADDFF), onPrimaryContainer = Color(0xFF21005D), secondary = Color(0xFF625B71), onSecondary = Color.White, tertiary = Color(0xFF7D5260), onTertiary = Color.White, surface = Color(0xFFFFFBFE), onSurface = Color(0xFF1C1B1F), )

Typography:

val AppTypography = Typography( displayLarge = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, fontSize = 57.sp, lineHeight = 64.sp ), headlineMedium = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, fontSize = 28.sp, lineHeight = 36.sp ), titleLarge = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, fontSize = 22.sp, lineHeight = 28.sp ), bodyLarge = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, fontSize = 16.sp, lineHeight = 24.sp ), labelMedium = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Medium, fontSize = 12.sp, lineHeight = 16.sp ) )

  1. Component Examples

Cards:

@Composable fun FeatureCard( title: String, description: String, imageUrl: String, onClick: () -> Unit ) { Card( onClick = onClick, modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.surfaceVariant ) ) { Column { AsyncImage( model = imageUrl, contentDescription = null, modifier = Modifier .fillMaxWidth() .height(180.dp), contentScale = ContentScale.Crop ) Column(modifier = Modifier.padding(16.dp)) { Text( text = title, style = MaterialTheme.typography.titleMedium ) Spacer(modifier = Modifier.height(8.dp)) Text( text = description, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } } }

Buttons:

// Filled button (primary action) Button(onClick = { }) { Text("Continue") }

// Filled tonal button (secondary action) FilledTonalButton(onClick = { }) { Icon(Icons.Default.Add, null) Spacer(Modifier.width(8.dp)) Text("Add Item") }

// Outlined button OutlinedButton(onClick = { }) { Text("Cancel") }

// Text button TextButton(onClick = { }) { Text("Learn More") }

// FAB FloatingActionButton( onClick = { }, containerColor = MaterialTheme.colorScheme.primaryContainer, contentColor = MaterialTheme.colorScheme.onPrimaryContainer ) { Icon(Icons.Default.Add, contentDescription = "Add") }

Quick Start Component @Composable fun ItemListCard( item: Item, onItemClick: () -> Unit, modifier: Modifier = Modifier ) { Card( onClick = onItemClick, modifier = modifier.fillMaxWidth(), shape = RoundedCornerShape(12.dp) ) { Row( modifier = Modifier .padding(16.dp) .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { Box( modifier = Modifier .size(48.dp) .clip(CircleShape) .background(MaterialTheme.colorScheme.primaryContainer), contentAlignment = Alignment.Center ) { Icon( imageVector = Icons.Default.Star, contentDescription = null, tint = MaterialTheme.colorScheme.onPrimaryContainer ) }

        Spacer(modifier = Modifier.width(16.dp))

        Column(modifier = Modifier.weight(1f)) {
            Text(
                text = item.title,
                style = MaterialTheme.typography.titleMedium
            )
            Text(
                text = item.subtitle,
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )
        }

        Icon(
            imageVector = Icons.Default.ChevronRight,
            contentDescription = null,
            tint = MaterialTheme.colorScheme.onSurfaceVariant
        )
    }
}

}

Best Practices Use Material Theme: Access colors via MaterialTheme.colorScheme for automatic dark mode support Support Dynamic Color: Enable dynamic color on Android 12+ for personalization Adaptive Layouts: Use WindowSizeClass for responsive designs Content Descriptions: Add contentDescription to all interactive elements Touch Targets: Minimum 48dp touch targets for accessibility State Hoisting: Hoist state to make components reusable and testable Remember Properly: Use remember and rememberSaveable appropriately Preview Annotations: Add @Preview with different configurations Common Issues Recomposition Issues: Avoid passing unstable lambdas; use remember State Loss: Use rememberSaveable for configuration changes Performance: Use LazyColumn instead of Column for long lists Theme Leaks: Ensure MaterialTheme wraps all composables Navigation Crashes: Handle back press and deep links properly Memory Leaks: Cancel coroutines in DisposableEffect Resources Material Design 3 Jetpack Compose Documentation Compose Samples Material 3 Compose

返回排行榜