i want to share image of my component , but cannot accses view of it, to share.
how i can accses view of my component or share image of it.
my component is a card
#Composable
fun CardItem(modifier: Modifier=Modifier,card: Card, onClick:()->Unit) {
Box(
modifier
.background(
if (card.card_number == AllCardsActivity.NOCARD) MaterialTheme.colorScheme.secondary
else MaterialTheme.colorScheme.primary,
RoundedCornerShape(20.dp)
)
.width(CardWidth)
) {
Column(Modifier.padding(25.dp)) {....
i want to share image of my component
If you mean that you want to capture a Bitmap of a composable for the purposes of sharing it, there are at least three libraries for this:
https://github.com/PatilShreyas/Capturable
https://github.com/KaustubhPatange/kapture
https://github.com/SmartToolFactory/Compose-Screenshot
And all three are open source, so if you do not like them for some reason, you can examine their implementations, contribute back changes, etc.
Related
Good Morning, I'm developing a new Wear OS application. The thing is, I can't find anywhere how to make a good pagination.
I would like to change activity on swipe (no matter if it's vertical or horizontal) but can't find anywhere how to do it...
Maybe I should not navigate in my app like that ?
Can you help me if you have any answer on how to do it ?
thanks ! (for some reasons I can't say hello on my post ?)
You can use HorizontalPager or VerticalPager from Accompanist.
https://google.github.io/accompanist/pager/
HorizontalPager(
modifier = Modifier.fillMaxSize(),
count = 10,
state = state
) { page ->
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(text = "Screen $page")
}
}
An example https://github.com/google/horologist/blob/e2741cef87774b18d58bb1b0f78bd5b60901f20d/sample/src/main/java/com/google/android/horologist/scratch/ScratchActivity.kt#L79
If you want something more custom, then it's probably a serious investment in a component. Especially if it needs to work with the system Swipe to Dismiss.
I am familiar with the Transition API in Jetpack Compose that lets me easily animate between two states inside a Compose component.
But what would be the best way to animate between three different states?
Consider as an example a loading indicator for a component. The states could be NotLoading, Loading and HasLoaded. My thinking here would be to have a loading indicator for a component and transition between those states:
Transition for showing the loading indicator: NotLoading -> Loading
Transition for showing the data: Loading -> HasLoaded
I guess what kind of transition doesn't really matter but I was thinking first fade in loading indicator then fade out loading indicator and fade in content. But this is just an example; in reality I need to specify the transition parameters.
What would be the best way to achieve this with Jetpack Compose? Not sure if my state thinking here is the best approach for this either.
You can use the Transition API with more than 2 states - and define the individual properties of each component using animate*AsState APIs.
There is another option if you have completely different Composables, you can use the AnimatedContent APIs.
For example, the below sample uses an enum UiState, and a button to change between the states. The content is then wrapped inside the AnimatedContent() composable. By default, the initial content fades out and then the target content fades in (this behavior is called fade through).
#Composable
fun AnimatedContentStateExample() {
Column {
var state by remember { mutableStateOf(UiState.Loading) }
Button(onClick = {
state = when (state) {
UiState.Loading -> UiState.Loaded
UiState.Loaded -> UiState.Empty
UiState.Empty -> UiState.Loading
}
}) {
Text("Switch States")
}
AnimatedContent(
targetState = state
) { targetState ->
// Make sure to use `targetState`, not `state`.
when (targetState) {
UiState.Loading -> {
CircularProgressIndicator()
}
UiState.Loaded -> {
Box(
Modifier
.background(androidGreen)
.size(100.dp))
Text("Loaded")
}
UiState.Empty -> {
Box(
Modifier
.background(androidBlue)
.size(200.dp))
Text("Empty")
}
}
}
}
}
You can customize this animation behavior by specifying a ContentTransform object to the transitionSpec parameter. You can create ContentTransform by combining an EnterTransition with an ExitTransition using the with infix function. You can apply SizeTransform to the ContentTransform by attaching it with the using infix function.
More information about AnimatedContent can be found here: https://developer.android.com/jetpack/compose/animation#animatedcontent.
And samples of its usage here: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedContentSamples.kt
I have already viewed other posts on the site. They suggest using the focusRequestor modifier and I've tried that.
val scope = rememberCoroutineScope()
val focusRequester = remember { FocusRequester() }
Text(
modifier = Modifier
.focusable()
.focusTarget() //Tried Adding/Removing
.onFocusChanged {
if (it.hasFocus || it.isFocused) {
Log.i("Test", "Text Focused")
}
} // Tried Adding Visual Elements to delete it as well.
.focusRequester(focusRequester),
text = "MARSK"
)
LaunchedEffect(scope) {
focusRequester.requestFocus()
}
I'm taking Text in this example, but actually I need to implement this on Canvas. This approach is not working on Box too. I'm afraid the same is the case for other containers as well.
For background, this is being built for my TV, so I wish to make this element clickable by the OK button on the remote's D-Pad. My approach is to make it focused first, then assuming that once it is focused, it will automatically detect the press of the OK button as the 'click'. If there is something wrong with my approach, feedback for improvements is also welcome.
PS: The solution by Abhimanyu works like a charm on Mobile Devices. However, since I mentioned a TV above, a TV is the device in consideration. On the TV, I have to press a button (Any button on the DPAD works, even the OK button, strangely) to get it focused up. Any idea how to get round this issue?
Thanks
Issue
The onFocusChanged should be added BEFORE the focusTarget or focusable that is being observed.
Source
Changes
Remove focusTarget and move onFocusChanged before focusable.
Also, note that focusRequester must be before focusable.
This would hopefully work for all comopsables. I have tested using Text and the example from Docs was using Box.
Extra details.
Prefer focusable over focusTarget.
From focusTarget docs,
Note: This is a low level modifier. Before using this consider using Modifier.focusable(). It uses a focusTarget in its implementation. Modifier.focusable() adds semantics that are needed for accessibility.
Order of Modifiers matter
Layouts in Jetpack Compose Codelab
Check Order Matters section
order matters when chaining modifiers as they're applied to the composable they modify from earlier to later,
Sample code
#Composable
fun FocusableText() {
val scope = rememberCoroutineScope()
val focusRequester = remember { FocusRequester() }
var color by remember { mutableStateOf(White) }
LaunchedEffect(scope) {
focusRequester.requestFocus()
}
Text(
modifier = Modifier
.background(color)
.onFocusChanged {
color = if (it.hasFocus || it.isFocused) {
LightGray
} else {
White
}
}
.focusRequester(focusRequester)
.focusable(),
text = "Test Text"
)
}
The Text background is LightGray which indicates the text is focused.
I am using a BottomNavigation with 4 composables. All of them have a LazyColumn with each item in the LazyColumn having an image populated from the network using Coil for Jetpack Compose. Similar to Twitter/YouTube.
When I navigate between these items, the composables get destroyed and recompose only when navigated back to them. Even the coil images are cleared and re-fetched (from memory or local storage) when navigated between these composables. This is of course the expected behavior.
The problem is that this is causing the navigation between them to be too slow. Coil images take about 400ms to 700ms to load the image for every navigation. Apps like YouTube/LinkedIn are literally instant in their BottomBar navigations.
When I was using XML for this, I would make the fragments(used as bottom nav items ) with an appear/disappear logic to avoid this time delay while navigating between them.
How do I achieve the same with Compose ?
I am using the following versions:
//compose navigation
implementation "androidx.navigation:navigation-compose:2.4.0-beta01"
implementation "com.google.accompanist:accompanist-navigation-animation:0.21.0-beta"
Well I had the same problem using emulator or phone both work well with small simplistic composables. And when I create more complex Composable and try animating the navigation, using the accompanist animation library it gets very laggy.
But then I tried building the release APK file, since it is usually optimized and way faster, and that will significantly speed up your app. Here is link on how you can generate signed APK then install it on your phone or emulator and see if that fixes your problem!
You could also check if you accidentally disabled the hardware acceleration from the manifest. Make sure you set android:hardwareAccelerated="true" in you activity tag.
In case that does not help either, then you have to implement your own animation and use Shared ViewModel, for communication and triggering the transition from one Composable to another. The idea is that you can use modifier offset property to show/hide the Composable by placing it outside the screen.
First set your ViewModel, and add mutable state variable, that will trigger the transition from Home to Settings and vice versa.
This is not the best practice, since there is no way to directly pass data from one composable to another as you would normally do with the normal navigation. But you can still share data using the Shared ViewModel. Using this method it will not recompose your Composable, and thus be really fast. So far I have no problem with any out of memory exceptions even on some very old/slow devices with 2GB RAM.
class SharedViewModel : ViewModel() {
// changing this mutable state will trigger the transition animation
private val _switchHomeToSetting = mutableStateOf(true)
val switchHomeToSetting: State<Boolean> = _switchHomeToSetting
fun switchHomeToSettings() {
_switchHomeToSetting.value = !_switchHomeToSetting.value
}
}
Now create your two Composable functions Home and Settings respectively
#Composable
fun HomeScreen(viewModel: SharedViewModel) {
// draw your subcomponents
}
#Composable
fun SettingsScreen(viewModel: SharedViewModel) {
// draw your subcomponents
}
And finally initialize the animation in you main activity
class MainActivity : ComponentActivity() {
val viewModel by viewModels<CityWeatherViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// set your offset animation
val density = LocalDensity.current
val width = with(density) { (1.adw).toPx() }
val animatedOffsetX: Float by animateFloatAsState(
targetValue = if (!viewModel.switchHomeToSetting.value) 0f else width,
animationSpec = tween(1200)
)
// Home screen
Box(
modifier = Modifier
.fillMaxSize()
.offset { IntOffset((-width + animatedOffsetX).toInt(), 0) }
) {
HomeScreen(viewModel = viewModel)
}
// Settings screen
Box(
modifier = Modifier
.fillMaxSize()
.offset { IntOffset(animatedOffsetX.toInt(), 0) }
) {
SettingsScreen(viewModel = viewModel)
}
}
}
}
Here is the result using the Composable Navigation, together with the Accompanist Animation. As you can see it is very laggy indeed
And now here is the result using our custom animation, where it is very smooth since no Composable is recomposed.
I am trying to have a way to show a BottomSheet from everywhere within my app, for that I use the BottomSheetScaffold and a LiveData object that holds the current composable function which is observed as state:
val sheetContent by MusicHub.state.bottomSheet.getContent().observeAsState()
BottomSheetScaffold(
modifier = Modifier
.fillMaxSize(),
scaffoldState = bottomSheetScaffoldState,
sheetPeekHeight = 0.dp,
sheetContent = sheetContent!!
)
I use the BottomSheet as a context menu in my app. For example when i longClick on a playlist in my app it sets the content of the BottomSheet and shows it like this:
PlaylistItem(
// ...
onLongClick = {
// Set the LiveData composable
MusicHub.state.bottomSheet.setContent {
PlaylistContextMenuTest(playlist!!, viewModel)
}
// Expand BottomSheet
scope.launch {
MusicHub.state.bottomSheet.expand()
}
}
)
In general this works but the first time the BottomSheet gets expanded it shows for a split second before it disappears at the bottom again. Here is a small GIF:
My guess is that somehow the size of the BottomSheet is not recalculated yet and hence it only works in the next recomposition. Coming from web dev i would say its a typical case of requestAnimationFrame but i don't quite know how to solve this issue in compose.
Edit:
PlaylistContextMenuTest code:
#Composable
fun PlaylistContextMenuTest(playlist: Playlist, viewModel: LibraryViewModel = activityViewModel()){
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.fillMaxWidth()
.navigationBarsPadding()
// TODO: Replace that with a percentage of the screen height
.heightIn(max = 384.dp)
.verticalScroll(scrollState),
content = {
ContextMenu {
repeat(4){
addOption(R.drawable.ic_delete_black_24dp, "Delete"){
Timber.d("Delete Playlist")
viewModel.deletePlaylist(playlist)
}
}
}
}
)
}
Full ContextMenu source: (https://pastebin.com/sg4ed96L)
You seem to be right. The first time it seems to be re-calculating the BottomSheet's height.
As this does not change on the second try, it will then be displayed.
Does this answer help you?
It seems to work out quite okay for BottomSheetScaffold, but not for ModalBottomSheetLayout (the thing I was looking for as you can see in the comments of the linked answer).
Edit: Accompanist's Navigation Material is also worth a look.
This issue has been resolved with a new release of jetpack compose. I am not sure if it was beta 8 or 9 but everything works as intended now.