Skip to content

Commit ed88a47

Browse files
Merge branch 'feature/demo-image' into develop
2 parents 564152f + e55b967 commit ed88a47

File tree

11 files changed

+1221
-14
lines changed

11 files changed

+1221
-14
lines changed

app/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ dependencies {
5353
implementation 'androidx.core:core-ktx:1.8.0'
5454
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
5555

56+
implementation 'com.github.SmartToolFactory:Compose-Extended-Gestures:1.2.0'
57+
5658
implementation "androidx.compose.ui:ui:$compose_version"
5759
// Tooling support (Previews, etc.)
5860
implementation "androidx.compose.ui:ui-tooling:$compose_version"
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package com.smarttoolfactory.composeimage
2+
3+
import androidx.compose.foundation.layout.Row
4+
import androidx.compose.foundation.layout.fillMaxWidth
5+
import androidx.compose.foundation.layout.padding
6+
import androidx.compose.material3.*
7+
import androidx.compose.runtime.*
8+
import androidx.compose.ui.Alignment
9+
import androidx.compose.ui.Modifier
10+
import androidx.compose.ui.graphics.Color
11+
import androidx.compose.ui.layout.ContentScale
12+
import androidx.compose.ui.text.TextStyle
13+
import androidx.compose.ui.text.font.FontWeight
14+
import androidx.compose.ui.unit.dp
15+
import androidx.compose.ui.unit.sp
16+
17+
val contentScaleOptions =
18+
listOf("None", "Fit", "Crop", "FillBounds", "FillWidth", "FillHeight", "Inside")
19+
20+
@Composable
21+
fun ContentScaleSelectionMenu(
22+
contentScale: ContentScale,
23+
onContentScaleChanged: (ContentScale) -> Unit
24+
) {
25+
var index = when (contentScale) {
26+
ContentScale.None -> 0
27+
ContentScale.Fit -> 1
28+
ContentScale.Crop -> 2
29+
ContentScale.FillBounds -> 3
30+
ContentScale.FillWidth -> 4
31+
ContentScale.FillHeight -> 5
32+
else -> 6
33+
}
34+
35+
Row(
36+
verticalAlignment = Alignment.CenterVertically,
37+
modifier = Modifier
38+
.fillMaxWidth()
39+
.padding(2.dp)
40+
) {
41+
ExposedSelectionMenu(
42+
modifier = Modifier.fillMaxWidth(),
43+
index = index,
44+
title = "ContentScale",
45+
options = contentScaleOptions,
46+
onSelected = {
47+
index = it
48+
val scale = when (index) {
49+
0 -> ContentScale.None
50+
1 -> ContentScale.Fit
51+
2 -> ContentScale.Crop
52+
3 -> ContentScale.FillBounds
53+
4 -> ContentScale.FillWidth
54+
5 -> ContentScale.FillHeight
55+
else -> ContentScale.Inside
56+
}
57+
58+
onContentScaleChanged(scale)
59+
}
60+
)
61+
}
62+
}
63+
64+
@OptIn(ExperimentalMaterial3Api::class)
65+
@Composable
66+
fun ExposedSelectionMenu(
67+
modifier: Modifier = Modifier,
68+
index: Int,
69+
title: String? = null,
70+
textStyle: TextStyle = TextStyle(
71+
fontWeight = FontWeight.W600,
72+
fontSize = 14.sp
73+
),
74+
colors: TextFieldColors = ExposedDropdownMenuDefaults.textFieldColors(
75+
containerColor = Color.Transparent,
76+
focusedIndicatorColor = Color.Transparent,
77+
unfocusedIndicatorColor = Color.Transparent,
78+
disabledIndicatorColor = Color.Transparent
79+
),
80+
options: List<String>,
81+
onSelected: (Int) -> Unit
82+
) {
83+
84+
var expanded by remember { mutableStateOf(false) }
85+
var selectedOptionText by remember { mutableStateOf(options[index]) }
86+
87+
ExposedDropdownMenuBox(
88+
modifier = modifier,
89+
expanded = expanded,
90+
onExpandedChange = {
91+
expanded = !expanded
92+
}
93+
) {
94+
TextField(
95+
modifier = modifier,
96+
readOnly = true,
97+
value = selectedOptionText,
98+
onValueChange = { },
99+
label = {
100+
title?.let {
101+
Text(it)
102+
}
103+
},
104+
trailingIcon = {
105+
ExposedDropdownMenuDefaults.TrailingIcon(
106+
expanded = expanded
107+
)
108+
},
109+
colors = colors,
110+
textStyle = textStyle
111+
)
112+
ExposedDropdownMenu(
113+
expanded = expanded,
114+
onDismissRequest = {
115+
expanded = false
116+
117+
}
118+
) {
119+
options.forEachIndexed { index: Int, selectionOption: String ->
120+
DropdownMenuItem(
121+
onClick = {
122+
selectedOptionText = selectionOption
123+
expanded = false
124+
onSelected(index)
125+
},
126+
text = {
127+
Text(text = selectionOption)
128+
}
129+
)
130+
}
131+
}
132+
}
133+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.smarttoolfactory.composeimage
2+
3+
import android.annotation.SuppressLint
4+
import android.graphics.Bitmap
5+
import android.graphics.ImageDecoder
6+
import android.os.Build
7+
import android.provider.MediaStore
8+
import androidx.activity.compose.rememberLauncherForActivityResult
9+
import androidx.compose.material.icons.Icons
10+
import androidx.compose.material.icons.filled.Add
11+
import androidx.compose.material3.FloatingActionButton
12+
import androidx.compose.material3.FloatingActionButtonDefaults
13+
import androidx.compose.material3.FloatingActionButtonElevation
14+
import androidx.compose.material3.Icon
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.ui.graphics.ImageBitmap
17+
import androidx.compose.ui.graphics.asImageBitmap
18+
import androidx.compose.ui.platform.LocalContext
19+
import com.google.modernstorage.photopicker.PhotoPicker
20+
21+
@SuppressLint("UnsafeOptInUsageError")
22+
@Composable
23+
fun ImageSelectionButton(
24+
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
25+
onImageSelected: (ImageBitmap) -> Unit
26+
) {
27+
val context = LocalContext.current
28+
29+
val photoPicker =
30+
rememberLauncherForActivityResult(PhotoPicker()) { uris ->
31+
val uri = uris.firstOrNull() ?: return@rememberLauncherForActivityResult
32+
33+
val bitmap: Bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
34+
ImageDecoder.decodeBitmap(
35+
ImageDecoder.createSource(context.contentResolver, uri)
36+
) { decoder, info, source ->
37+
decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
38+
decoder.isMutableRequired = true
39+
}
40+
} else {
41+
MediaStore.Images.Media.getBitmap(context.contentResolver, uri)
42+
}
43+
44+
onImageSelected(bitmap.asImageBitmap())
45+
46+
}
47+
48+
FloatingActionButton(
49+
elevation = elevation,
50+
onClick = {
51+
photoPicker.launch(PhotoPicker.Args(PhotoPicker.Type.IMAGES_ONLY, 1))
52+
},
53+
) {
54+
Icon(
55+
imageVector = Icons.Default.Add,
56+
contentDescription = null
57+
)
58+
}
59+
}
Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
1+
@file:OptIn(ExperimentalPagerApi::class, ExperimentalMaterial3Api::class)
2+
13
package com.smarttoolfactory.composeimage
24

35
import android.os.Bundle
46
import androidx.activity.ComponentActivity
57
import androidx.activity.compose.setContent
68
import androidx.compose.foundation.layout.fillMaxSize
7-
import androidx.compose.material3.MaterialTheme
8-
import androidx.compose.material3.Surface
9-
import androidx.compose.material3.Text
9+
import androidx.compose.foundation.layout.fillMaxWidth
10+
import androidx.compose.foundation.layout.padding
11+
import androidx.compose.material3.*
12+
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
1013
import androidx.compose.runtime.Composable
14+
import androidx.compose.runtime.rememberCoroutineScope
1115
import androidx.compose.ui.Modifier
12-
import androidx.compose.ui.tooling.preview.Preview
16+
import androidx.compose.ui.unit.dp
17+
import com.google.accompanist.pager.ExperimentalPagerApi
18+
import com.google.accompanist.pager.HorizontalPager
19+
import com.google.accompanist.pager.PagerState
20+
import com.google.accompanist.pager.rememberPagerState
21+
import com.smarttoolfactory.composeimage.demo.ImageWithConstraintsDemo
22+
import com.smarttoolfactory.composeimage.demo.ThumbnailDemo
1323
import com.smarttoolfactory.composeimage.ui.theme.ComposeImageTheme
24+
import kotlinx.coroutines.launch
1425

1526
class MainActivity : ComponentActivity() {
1627
override fun onCreate(savedInstanceState: Bundle?) {
@@ -22,22 +33,70 @@ class MainActivity : ComponentActivity() {
2233
modifier = Modifier.fillMaxSize(),
2334
color = MaterialTheme.colorScheme.background
2435
) {
25-
Greeting("Android")
36+
HomeContent()
2637
}
2738
}
2839
}
2940
}
3041
}
3142

43+
@ExperimentalPagerApi
3244
@Composable
33-
fun Greeting(name: String) {
34-
Text(text = "Hello $name!")
35-
}
45+
private fun HomeContent() {
3646

37-
@Preview(showBackground = true)
38-
@Composable
39-
fun DefaultPreview() {
40-
ComposeImageTheme {
41-
Greeting("Android")
47+
val pagerState: PagerState = rememberPagerState(initialPage = 0)
48+
49+
val coroutineScope = rememberCoroutineScope()
50+
51+
Scaffold(
52+
topBar = {
53+
ScrollableTabRow(
54+
modifier = Modifier.fillMaxWidth(),
55+
// Our selected tab is our current page
56+
selectedTabIndex = pagerState.currentPage,
57+
// Override the indicator, using the provided pagerTabIndicatorOffset modifier
58+
indicator = { tabPositions: List<TabPosition> ->
59+
TabRowDefaults.Indicator(
60+
modifier = Modifier.tabIndicatorOffset(
61+
tabPositions[pagerState.currentPage]
62+
),
63+
height = 4.dp
64+
)
65+
},
66+
edgePadding = 4.dp
67+
) {
68+
// Add tabs for all of our pages
69+
tabList.forEachIndexed { index, title ->
70+
Tab(
71+
text = { Text(title) },
72+
selected = pagerState.currentPage == index,
73+
onClick = {
74+
coroutineScope.launch {
75+
pagerState.animateScrollToPage(index)
76+
}
77+
}
78+
)
79+
}
80+
}
81+
}
82+
) {
83+
84+
HorizontalPager(
85+
modifier = Modifier.padding(it),
86+
state = pagerState,
87+
count = tabList.size
88+
) { page: Int ->
89+
90+
when (page) {
91+
0 -> ImageWithConstraintsDemo()
92+
else-> ThumbnailDemo()
93+
}
94+
}
4295
}
43-
}
96+
}
97+
98+
internal val tabList =
99+
listOf(
100+
"Image Constraints",
101+
"Image Thumbnail"
102+
)

0 commit comments

Comments
 (0)