feat:photoPicker

This commit is contained in:
zixiaoweng
2022-09-19 16:24:18 +08:00
parent 635b4aa86a
commit 91dfc2506d
10 changed files with 143 additions and 74 deletions
@@ -28,11 +28,15 @@ import androidx.lifecycle.Lifecycle;
import com.qmuiteam.qmui.widget.QMUIPagerAdapter;
import java.util.HashMap;
import java.util.Map;
public abstract class QMUIFragmentPagerAdapter extends QMUIPagerAdapter {
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurrentTransaction;
private Fragment mCurrentPrimaryItem = null;
private Map map = new HashMap<Integer, QMUIFragment>();
public QMUIFragmentPagerAdapter(@NonNull FragmentManager fm) {
mFragmentManager = fm;
@@ -48,16 +52,17 @@ public abstract class QMUIFragmentPagerAdapter extends QMUIPagerAdapter {
@SuppressLint("CommitTransaction")
@Override
@NonNull
protected Object hydrate(@NonNull ViewGroup container, int position) {
public Object hydrate(@NonNull ViewGroup container, int position) {
String name = makeFragmentName(container.getId(), position);
if (mCurrentTransaction == null) {
mCurrentTransaction = mFragmentManager.beginTransaction();
}
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
return fragment;
if (fragment == null) {
fragment = createFragment(position);
}
return createFragment(position);
map.put(position, fragment);
return fragment;
}
@SuppressLint("CommitTransaction")
@@ -135,4 +140,8 @@ public abstract class QMUIFragmentPagerAdapter extends QMUIPagerAdapter {
private String makeFragmentName(int viewId, long id) {
return "QMUIFragmentPagerAdapter:" + viewId + ":" + id;
}
public QMUIFragment getFragment(int position) {
return (QMUIFragment) map.get(position);
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ buildscript {
}
dependencies {
classpath("com.android.tools.build:gradle:7.1.3")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.20")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
}
}
@@ -191,10 +191,10 @@ open class QMUIPhotoPickerActivity : AppCompatActivity() {
override fun handleOnBackPressed() {
when (viewModel.photoPickerSceneFlow.value) {
is QMUIPhotoPickerEditScene -> {
viewModel.updateScene(viewModel.prevScene ?: QMUIPhotoPickerGridScene)
viewModel.updateScene(viewModel.prevScene ?: QMUIPhotoPickerGridScene())
}
is QMUIPhotoPickerPreviewScene -> {
viewModel.updateScene(QMUIPhotoPickerGridScene)
viewModel.updateScene(viewModel.prevScene ?: QMUIPhotoPickerGridScene())
}
else -> {
isEnabled = false
@@ -223,7 +223,10 @@ open class QMUIPhotoPickerActivity : AppCompatActivity() {
}
@Composable
protected open fun BoxScope.PhotoPicker(viewModel: QMUIPhotoPickerViewModel) {
protected open fun BoxScope.PhotoPicker(
viewModel: QMUIPhotoPickerViewModel,
onPickItemBeyondLimit: () -> Unit = {}
) {
val data by viewModel.photoPickerDataFlow.collectAsState()
when (data.state) {
QMUIPhotoPickerLoadState.dataLoading,
@@ -241,7 +244,7 @@ open class QMUIPhotoPickerActivity : AppCompatActivity() {
} else if (list == null || list.isEmpty()) {
PageEmpty()
} else {
PhotoPickerContent(viewModel, list)
PhotoPickerContent(viewModel, list, onPickItemBeyondLimit)
}
}
}
@@ -251,7 +254,8 @@ open class QMUIPhotoPickerActivity : AppCompatActivity() {
@Composable
protected open fun BoxScope.PhotoPickerContent(
viewModel: QMUIPhotoPickerViewModel,
data: List<QMUIMediaPhotoBucketVO>
data: List<QMUIMediaPhotoBucketVO>,
onPickItemBeyondLimit: () -> Unit
) {
val pickedItems by viewModel.pickedListFlow.collectAsState()
val sceneState = viewModel.photoPickerSceneFlow.collectAsState()
@@ -262,7 +266,14 @@ open class QMUIPhotoPickerActivity : AppCompatActivity() {
enter = fadeIn(),
exit = fadeOut()
) {
PhotoPickerGridScene(viewModel, data, pickedItems)
val previewSceneHolder = remember {
SceneHolder(scene as? QMUIPhotoPickerGridScene)
}
if(scene is QMUIPhotoPickerGridScene){
previewSceneHolder.scene = scene
}
val previewScene = previewSceneHolder.scene
PhotoPickerGridScene(viewModel, previewScene ?: QMUIPhotoPickerGridScene(), data, pickedItems, onPickItemBeyondLimit)
}
AnimatedVisibility(
visible = scene is QMUIPhotoPickerPreviewScene,
@@ -302,8 +313,10 @@ open class QMUIPhotoPickerActivity : AppCompatActivity() {
@Composable
protected open fun BoxScope.PhotoPickerGridScene(
viewModel: QMUIPhotoPickerViewModel,
scene: QMUIPhotoPickerGridScene,
data: List<QMUIMediaPhotoBucketVO>,
pickedItems: List<Long>,
onPickItemBeyondLimit: () -> Unit,
topBarBackItem: QMUITopBarItem = remember {
QMUITopBarBackIconItem {
finish()
@@ -316,7 +329,7 @@ open class QMUIPhotoPickerActivity : AppCompatActivity() {
}
var currentBucket by remember {
mutableStateOf(data.first())
mutableStateOf(scene.buckedId?.let { buckedId -> data.first { it.id == buckedId } } ?: data.first())
}
val scrollState = viewModel.gridSceneScrollState
@@ -380,10 +393,12 @@ open class QMUIPhotoPickerActivity : AppCompatActivity() {
bottom.linkTo(toolbar.top)
},
state = scrollState,
pickLimitCount = viewModel.pickLimitCount,
pickedItems = pickedItems,
onPickItem = { _, model ->
viewModel.togglePick(model)
},
onPickItemBeyondLimit = onPickItemBeyondLimit,
onPreview = {
viewModel.updateScene(QMUIPhotoPickerPreviewScene(currentBucket.id, false, it.id))
}
@@ -412,6 +427,7 @@ open class QMUIPhotoPickerActivity : AppCompatActivity() {
onBucketClick = {
currentBucket = it
isFocusBucketFlow.value = false
viewModel.updateScene(QMUIPhotoPickerGridScene(currentBucket.id))
}) {
isFocusBucketFlow.value = false
}
@@ -466,7 +482,7 @@ open class QMUIPhotoPickerActivity : AppCompatActivity() {
val topBarLeftItems = remember {
arrayListOf<QMUITopBarItem>(QMUITopBarBackIconItem {
viewModel.updateScene(QMUIPhotoPickerGridScene)
viewModel.updateScene(viewModel.prevScene ?: QMUIPhotoPickerGridScene())
})
}
@@ -567,7 +583,7 @@ open class QMUIPhotoPickerActivity : AppCompatActivity() {
WindowCompat.getInsetsController(window, window.decorView)?.hide(WindowInsetsCompat.Type.statusBars())
}
QMUIPhotoPickerEdit(onBackPressedDispatcher, scene.current) {
viewModel.updateScene(viewModel.prevScene ?: QMUIPhotoPickerGridScene)
viewModel.updateScene(viewModel.prevScene ?: QMUIPhotoPickerGridScene())
}
}
@@ -1,5 +1,6 @@
package com.qmuiteam.photo.compose.picker
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.staticCompositionLocalOf
@@ -12,26 +13,26 @@ import com.qmuiteam.compose.core.ui.QMUITopBarItem
import com.qmuiteam.compose.core.ui.qmuiPrimaryColor
import kotlinx.coroutines.flow.StateFlow
class QMUIPhotoPickerConfig(
val editable: Boolean = true,
val primaryColor: Color = qmuiPrimaryColor,
val commonTextButtonTextColor: Color = Color.White,
val commonSeparatorColor: Color = Color.White.copy(alpha = 0.3f),
val commonIconNormalTintColor: Color = Color.White.copy(0.9f),
val commonIconCheckedTintColor: Color = primaryColor,
val commonIconCheckedTextColor: Color = Color.White.copy(alpha = 0.6f),
data class QMUIPhotoPickerConfig(
var editable: Boolean = true,
var primaryColor: Color = qmuiPrimaryColor,
var commonTextButtonTextColor: Color = Color.White,
var commonSeparatorColor: Color = Color.White.copy(alpha = 0.3f),
var commonIconNormalTintColor: Color = Color.White.copy(0.9f),
var commonIconCheckedTintColor: Color = primaryColor,
var commonIconCheckedTextColor: Color = Color.White.copy(alpha = 0.6f),
val commonButtonNormalTextColor: Color = Color.White,
val commonButtonNormalBgColor: Color = primaryColor,
val commonButtonDisabledTextColor: Color = Color.White.copy(alpha = 0.3f),
val commonButtonDisableBgColor: Color = Color.White.copy(alpha = 0.15f),
val commonButtonPressBgColor: Color = primaryColor.copy(alpha = 0.8f),
val commonButtonPressedTextColor: Color = commonButtonNormalTextColor,
var commonButtonNormalTextColor: Color = Color.White,
var commonButtonNormalBgColor: Color = primaryColor,
var commonButtonDisabledTextColor: Color = Color.White.copy(alpha = 0.3f),
var commonButtonDisableBgColor: Color = Color.White.copy(alpha = 0.15f),
var commonButtonPressBgColor: Color = primaryColor.copy(alpha = 0.8f),
var commonButtonPressedTextColor: Color = commonButtonNormalTextColor,
val topBarBgColor: Color = Color(0xFF222222),
val toolBarBgColor: Color = topBarBgColor,
var topBarBgColor: Color = Color(0xFF222222),
var toolBarBgColor: Color = topBarBgColor,
val topBarBucketFactory: (
var topBarBucketFactory: (
textFlow: StateFlow<String>,
isFocusFlow: StateFlow<Boolean>,
onClick: () -> Unit
@@ -46,7 +47,7 @@ class QMUIPhotoPickerConfig(
onClick = onClick
)
},
val topBarSendFactory: (
var topBarSendFactory: (
canSendSelf: Boolean,
maxSelectCount: Int,
selectCountFlow: StateFlow<Int>,
@@ -61,21 +62,21 @@ class QMUIPhotoPickerConfig(
)
},
val screenBgColor: Color = Color(0xFF333333),
val loadingColor: Color = Color.White,
val tipTextColor: Color = Color.White,
var screenBgColor: Color = Color(0xFF333333),
var loadingColor: Color = Color.White,
var tipTextColor: Color = Color.White,
val gridPreferredSize: Dp = 80.dp,
val gridGap: Dp = 2.dp,
val gridBorderColor: Color = Color.White.copy(alpha = 0.15f),
var gridPreferredSize: Dp = 80.dp,
var gridGap: Dp = 2.dp,
var gridBorderColor: Color = Color.White.copy(alpha = 0.15f),
val bucketChooserMaskColor: Color = Color.Black.copy(alpha = 0.36f),
val bucketChooserBgColor: Color = topBarBgColor,
val bucketChooserIndicationColor: Color = Color.White.copy(alpha = 0.2f),
val bucketChooserMainTextColor: Color = Color.White,
val bucketChooserCountTextColor: Color = Color.White.copy(alpha = 0.64f),
var bucketChooserMaskColor: Color = Color.Black.copy(alpha = 0.36f),
var bucketChooserBgColor: Color = topBarBgColor,
var bucketChooserIndicationColor: Color = Color.White.copy(alpha = 0.2f),
var bucketChooserMainTextColor: Color = Color.White,
var bucketChooserCountTextColor: Color = Color.White.copy(alpha = 0.64f),
val editPaintOptions: List<EditPaint> = listOf(
var editPaintOptions: List<EditPaint> = listOf(
MosaicEditPaint(16),
MosaicEditPaint(50),
ColorEditPaint(Color.White),
@@ -86,11 +87,11 @@ class QMUIPhotoPickerConfig(
ColorEditPaint(Color.Blue),
ColorEditPaint(Color.Magenta)
),
val graffitiPaintStrokeWidth: Dp = 5.dp,
val mosaicPaintStrokeWidth: Dp = 20.dp,
var graffitiPaintStrokeWidth: Dp = 5.dp,
var mosaicPaintStrokeWidth: Dp = 20.dp,
val textEditMaskColor:Color = Color.Black.copy(0.5f),
val textEditColorOptions: List<ColorEditPaint> = listOf(
var textEditMaskColor:Color = Color.Black.copy(0.5f),
var textEditColorOptions: List<ColorEditPaint> = listOf(
ColorEditPaint(Color.White),
ColorEditPaint(Color.Black),
ColorEditPaint(Color.Red),
@@ -99,20 +100,37 @@ class QMUIPhotoPickerConfig(
ColorEditPaint(Color.Blue),
ColorEditPaint(Color.Magenta)
),
val textEditFontSize: TextUnit = 30.sp,
val textEditLineSpace: TextUnit = 3.sp,
val textCursorColor: Color = primaryColor,
var textEditFontSize: TextUnit = 30.sp,
var textEditLineSpace: TextUnit = 3.sp,
var textCursorColor: Color = primaryColor,
val editLayerDeleteAreaNormalBgColor: Color = Color.Black.copy(alpha = 0.3f),
val editLayerDeleteAreaNormalFocusColor: Color = Color.Red.copy(alpha = 0.6f),
var editLayerDeleteAreaNormalBgColor: Color = Color.Black.copy(alpha = 0.3f),
var editLayerDeleteAreaNormalFocusColor: Color = Color.Red.copy(alpha = 0.6f),
var photoNotPickMaskColor: Color,
)
val qmuiPhotoPickerDefaultConfig by lazy { QMUIPhotoPickerConfig() }
val lightConfig by lazy { QMUIPhotoPickerConfig(photoNotPickMaskColor = Color(0x80FFFFFF)) }
val darkConfig by lazy { QMUIPhotoPickerConfig(photoNotPickMaskColor = Color(0x80000000)) }
val qmuiPhotoPickerDefaultConfig by lazy { lightConfig }
val QMUILocalPickerConfig = staticCompositionLocalOf { qmuiPhotoPickerDefaultConfig }
@Composable
fun QMUIDefaultPickerConfigProvider(content: @Composable () -> Unit) {
CompositionLocalProvider(QMUILocalPickerConfig provides qmuiPhotoPickerDefaultConfig) {
fun QMUIDefaultPickerConfigProvider(
darkTheme: Boolean = isSystemInDarkTheme(),
configSet: (QMUIPhotoPickerConfig) -> Unit = {},
content: @Composable () -> Unit
) {
val config = if (darkTheme) {
darkConfig
} else {
lightConfig
}.apply {
configSet(this)
}
CompositionLocalProvider(QMUILocalPickerConfig provides config) {
content()
}
}
@@ -55,8 +55,10 @@ fun QMUIPhotoPickerGrid(
data: List<QMUIMediaPhotoVO>,
modifier: Modifier = Modifier,
state: LazyListState = rememberLazyListState(),
pickLimitCount: Int,
pickedItems: List<Long>,
onPickItem: (toPick: Boolean, model: QMUIMediaPhotoVO) -> Unit,
onPickItemBeyondLimit: () -> Unit,
onPreview: (model: QMUIMediaModel) -> Unit
) {
BoxWithConstraints(modifier = modifier) {
@@ -79,7 +81,7 @@ fun QMUIPhotoPickerGrid(
verticalArrangement = Arrangement.Absolute.spacedBy(gap)
) {
items(rowData, key = { it.key }){ item ->
QMUIPhotoPickerGridRow(item, cellSize, gap, pickedItems, onPickItem, onPreview)
QMUIPhotoPickerGridRow(item, cellSize, gap, pickLimitCount, pickedItems, onPickItem, onPickItemBeyondLimit, onPreview)
}
}
}
@@ -90,8 +92,10 @@ private fun QMUIPhotoPickerGridRow(
data: QMUIPhotoPickerGridRowData,
cellSize: Dp,
gap: Dp,
pickLimitCount: Int,
pickedItems: List<Long>,
onPickItem: (toPick: Boolean, model: QMUIMediaPhotoVO) -> Unit,
onPickItemBeyondLimit: () -> Unit,
onPreview: (model: QMUIMediaModel) -> Unit
) {
Row(
@@ -103,8 +107,10 @@ private fun QMUIPhotoPickerGridRow(
QMUIPhotoPickerGridCell(
data = data.list[i],
cellSize = cellSize,
pickLimitCount = pickLimitCount,
pickedItems = pickedItems,
onPickItem = onPickItem,
onPickItemBeyondLimit = onPickItemBeyondLimit,
onPreview = onPreview
)
}
@@ -115,8 +121,10 @@ private fun QMUIPhotoPickerGridRow(
private fun QMUIPhotoPickerGridCell(
data: QMUIMediaPhotoVO,
cellSize: Dp,
pickLimitCount: Int,
pickedItems: List<Long>,
onPickItem: (toPick: Boolean, model: QMUIMediaPhotoVO) -> Unit,
onPickItemBeyondLimit: () -> Unit,
onPreview: (model: QMUIMediaModel) -> Unit
) {
val pickedIndex = remember(pickedItems) {
@@ -124,6 +132,9 @@ private fun QMUIPhotoPickerGridCell(
it == data.model.id
}
}
val pickedSize = remember(pickedItems) {
pickedItems.size
}
Box(
modifier = Modifier
.size(cellSize)
@@ -135,7 +146,9 @@ private fun QMUIPhotoPickerGridCell(
indication = null,
enabled = true
) {
onPreview.invoke(data.model)
if (!(pickedSize >= pickLimitCount && (pickedIndex < 0))) {
onPreview.invoke(data.model)
}
}
) {
val thumbnail = remember(data) {
@@ -148,13 +161,17 @@ private fun QMUIPhotoPickerGridCell(
onError = null
)
QMUIPhotoPickerGridCellMask(pickedIndex)
QMUIPhotoPickerGridCellMask(pickedIndex, pickedSize, pickLimitCount)
Box(
modifier = Modifier
.align(Alignment.TopEnd)
.clickable {
onPickItem(pickedIndex < 0, data)
if (pickedIndex < 0 && pickedSize >= pickLimitCount) {
onPickItemBeyondLimit()
} else {
onPickItem(pickedIndex < 0, data)
}
}
.padding(4.dp)
.size(24.dp),
@@ -166,12 +183,19 @@ private fun QMUIPhotoPickerGridCell(
}
@Composable
fun QMUIPhotoPickerGridCellMask(pickedIndex: Int){
val maskAlpha = animateFloatAsState(targetValue = if(pickedIndex >= 0) 0.36f else 0.15f)
fun QMUIPhotoPickerGridCellMask(pickedIndex: Int, pickedSize: Int, pickLimitCount: Int) {
val photoNotPickMaskColor = QMUILocalPickerConfig.current.photoNotPickMaskColor
val maskAlpha = animateFloatAsState(targetValue = if (pickedIndex >= 0) 0.36f else 0.15f)
val maskPickedAlpha = animateFloatAsState(targetValue = if (pickedSize >= pickLimitCount) photoNotPickMaskColor.alpha else 0.15f)
val bgColor = if (pickedSize >= pickLimitCount && (pickedIndex < 0)) {
photoNotPickMaskColor.copy(alpha = maskPickedAlpha.value)
} else {
Color.Black.copy(alpha = maskAlpha.value)
}
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = maskAlpha.value))
.background(bgColor)
)
}
@@ -31,7 +31,7 @@ class QMUIPhotoPickerViewModel @Keep constructor(
private val photoProviderFactory: QMUIMediaPhotoProviderFactory
private val _photoPickerSceneFlow = MutableStateFlow<QMUIPhotoPickerScene>(QMUIPhotoPickerGridScene)
private val _photoPickerSceneFlow = MutableStateFlow<QMUIPhotoPickerScene>(QMUIPhotoPickerGridScene())
val photoPickerSceneFlow = _photoPickerSceneFlow.asStateFlow()
val gridSceneScrollState = LazyListState()
@@ -155,7 +155,7 @@ class QMUIPhotoPickerViewModel @Keep constructor(
open class QMUIPhotoPickerScene
object QMUIPhotoPickerGridScene : QMUIPhotoPickerScene()
class QMUIPhotoPickerGridScene(val buckedId: String? = null) : QMUIPhotoPickerScene()
class QMUIPhotoPickerPreviewScene(
val buckedId: String,
+4 -4
View File
@@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
`java-gradle-plugin`
idea
kotlin("jvm") version "1.6.20"
kotlin("jvm") version "1.6.21"
`kotlin-dsl`
}
@@ -15,7 +15,7 @@ buildscript {
}
dependencies {
classpath("com.android.tools.build:gradle:7.1.3")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.20")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
}
}
@@ -45,8 +45,8 @@ repositories {
dependencies {
api(gradleApi())
api(gradleKotlinDsl())
api(kotlin("gradle-plugin", version = "1.6.20"))
api(kotlin("gradle-plugin-api", version = "1.6.20"))
api(kotlin("gradle-plugin", version = "1.6.21"))
api(kotlin("gradle-plugin-api", version = "1.6.21"))
api("com.android.tools.build:gradle-api:7.1.3")
api("com.android.tools.build:gradle:7.1.3")
}
@@ -14,13 +14,13 @@ object Dep {
object QMUI {
const val group = "com.qmuiteam"
const val qmuiVer = "2.1.0.11"
const val archVer = "2.1.0.3"
const val archVer = "2.1.0.4"
const val typeVer = "0.1.0.6"
// composeMajor.composeMinor.qmuiReleaseNumber
const val composeCoreVer = "1.1.1"
const val composeCoreVer = "1.1.1.1"
const val composeVer = "1.1.1.1"
const val photoVer = "1.1.1.1"
const val photoVer = "1.1.1.6"
const val editorVer = "1.1.1"
}
@@ -42,7 +42,7 @@ object Dep {
}
object Compose {
val version = "1.2.0-alpha08"
val version = "1.2.0-beta02"
val animation = "androidx.compose.animation:animation:$version"
val ui = "androidx.compose.ui:ui:$version"
val material = "androidx.compose.material:material:$version"
@@ -28,5 +28,4 @@
<color name="btn_filled_blue_bg_pressed">#243f55</color>
<color name="btn_filled_blue_bg_disabled">#80416f96</color>
</resources>
+3
View File
@@ -32,6 +32,7 @@
<attr name="app_skin_span_pressed_text_color" format="color"/>
<attr name="app_skin_span_normal_bg_color" format="color"/>
<attr name="app_skin_span_pressed_bg_color" format="color"/>
<attr name="app_skin_photo_can_not_pick_mask_color" format="color"/>
<attr name="app_skin_alpha_test" format="float"/>
@@ -161,6 +162,7 @@
<item name="app_skin_span_pressed_text_color">@color/qmui_config_color_50_white</item>
<item name="app_skin_span_normal_bg_color">@color/qmui_config_color_gray_4</item>
<item name="app_skin_span_pressed_bg_color">@color/qmui_config_color_gray_2</item>
<item name="app_skin_photo_can_not_pick_mask_color">@color/qmui_config_color_50_pure_black</item>
<item name="app_skin_alpha_test">0.5</item>
</style>
@@ -193,6 +195,7 @@
<item name="app_skin_btn_test_border_single">?attr/qmui_config_color_gray_1</item>
<item name="app_skin_progress_bar_bg_color">@color/qmui_config_color_gray_8</item>
<item name="app_skin_progress_bar_progress_color">@color/qmui_config_color_gray_1</item>
<item name="app_skin_photo_can_not_pick_mask_color">@color/qmui_config_color_50_white</item>
</style>
</resources>