Why aren't the radio buttons displayed when I use MaterialAlertDialogBuilder? - android-studio

The Code A is based the article.
And I get Image A when I run Code A, it seems that all the radio buttons aren't displayed .
What's wrong with my code?
Code A
val singleItems = arrayOf("Item 1", "Item 2", "Item 3")
val checkedItem = 1
val context = LocalContext.current
MaterialAlertDialogBuilder(context)
.setTitle("Title")
.setNeutralButton("Cancel") { dialog, which ->
}
.setPositiveButton("OK") { dialog, which ->
}
.setSingleChoiceItems(singleItems, checkedItem) { dialog, which ->
}
.show()
themes.xml
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.SoundMeter" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="colorPrimary">#color/purple_500</item>
<item name="colorPrimaryVariant">#color/purple_700</item>
<item name="colorOnPrimary">#color/white</item>
<item name="colorSecondary">#color/teal_200</item>
<item name="colorSecondaryVariant">#color/teal_700</item>
<item name="colorOnSecondary">#color/black</item>
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
</style>
<style name="Theme.SoundMeter.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="Theme.SoundMeter.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="Theme.SoundMeter.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>
Image A

Tried with the MaterialAlertDialogBuilder still the same issue.
So I created the dialog using the Dialog Composable
#Composable
fun CustomDialog(
title: String = "Title",
onDismiss: (() -> Unit)? = {},
radioItems: List<String> = listOf("Item 1", "Item 2", "Item 3"),
positiveAction: () -> Unit = {},
negativeAction: () -> Unit = {},
positiveText: String = "Ok",
cancelledText: String = "Cancel",
selectedIndex: MutableState<Int> = mutableStateOf(1),
changeIndex: (Int) -> Unit = { index -> selectedIndex.value = index }
) {
Dialog(onDismissRequest = { onDismiss?.invoke() }) {
Column(modifier = Modifier.background(Color.White)) {
Text(
text = title,
modifier = Modifier.padding(top = 16.dp, start = 16.dp, bottom = 16.dp),
fontWeight = FontWeight.Bold
)
radioItems.forEachIndexed { index, radioText ->
Row(
modifier = Modifier.fillMaxWidth().clickable { changeIndex(index) },
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = index == selectedIndex.value,
colors = RadioButtonDefaults.colors(selectedColor = Color.Blue),
onClick = { changeIndex(index) }
)
Text(text = radioText, modifier = Modifier.padding(start = 16.dp))
}
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
OutlinedButton(
onClick = {
negativeAction()
onDismiss?.invoke()
},
border = BorderStroke(0.dp, Color.Transparent),
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent)
) { Text(text = cancelledText, color = Color.Red) }
OutlinedButton(
onClick = {
positiveAction()
onDismiss?.invoke()
},
border = BorderStroke(0.dp, Color.Transparent),
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent)
) { Text(text = positiveText, color = Color.Blue) }
}
}
}
}
Screenshot

Related

Jetpack Compose view position and shadow preview is different to on when running on device

I have the below code, however the Preview is rendering the view different to when I run it on a AVD (Android Virtual Device). It looks there are 2 things different:
Firstly, the AVD is rending the view at the top of the screen, but the preview is rending the view in the centre of the screen. I've added contentAlignment = Alignment.Center to my AlertView Box which I believe should render it in the middle of the screen.
Secondly, the AVD is showing a weird shadow compared to the preview.
Images below of the preview and AVD:
//Preview Provider
#Preview(
showSystemUi = true,
device = Devices.PIXEL_4_XL,
uiMode = Configuration.UI_MODE_NIGHT_NO,
showBackground = true,
backgroundColor = 0xFFFFFFFF
)
#Preview(
showSystemUi = true,
device = Devices.PIXEL_4,
uiMode = Configuration.UI_MODE_NIGHT_YES,
showBackground = true,
backgroundColor = 0xFF000000
)
#Composable
fun DefaultPreview() {
AlertView()
}
//AlertViewModel
class AlertViewModel : ViewModel() {
var showAlert: Boolean by mutableStateOf(false)
var alertIcon: ImageVector by mutableStateOf(Icons.Rounded.WifiOff)
var alertTitle: String by mutableStateOf("No internet connection")
var alertMessage: String by mutableStateOf("This is the alert message and it is purposely longer than one line to be displayed on screen")
var alertButtonText: String by mutableStateOf("This is the alert button text")
}
//--------------------------------------------------------------------------------------------------
//.onCreate
class AlertManager : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AlertView()
}
}
}
//AlertView
#Composable
fun AlertView(alertViewModel: AlertViewModel = viewModel()) {
Box(
contentAlignment = Alignment.Center
) {
Column(
verticalArrangement = Arrangement.spacedBy(10.dp),
modifier = Modifier
.padding(30.dp, 0.dp)
.background(
shape = RoundedCornerShape(15.dp),
color =
if (isSystemInDarkTheme()) {
Color.Black
} else {
Color.White
}
)
.shadow(
elevation = 1.dp,
shape = RoundedCornerShape(15.dp),
ambientColor = if (isSystemInDarkTheme()) {
Color.Black
} else {
Color.White
},
spotColor = if (isSystemInDarkTheme()) {
Color.White
} else {
Color.Gray
},
)
.padding(30.dp, 25.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(20.dp)
) {
Icon(
imageVector = alertViewModel.alertIcon,
contentDescription = null,
tint = if (isSystemInDarkTheme()) {
Color.White
} else {
Color.Black
},
)
Text(
text = alertViewModel.alertTitle,
style = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold,
color = if (isSystemInDarkTheme()) {
Color.White
} else {
Color.Black
}
)
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(25.dp),
) {
Text(
text = alertViewModel.alertMessage,
color = if (isSystemInDarkTheme()) {
Color.White
} else {
Color.Black
}
)
Button(
onClick = {
alertViewModel.showAlert = false
},
colors = ButtonDefaults.buttonColors(
backgroundColor = if (isSystemInDarkTheme()) {
Color.White
} else {
Color.Black
}
),
shape = RoundedCornerShape(15.dp),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
contentPadding = PaddingValues(15.dp),
) {
Text(
text = alertViewModel.alertButtonText,
fontWeight = FontWeight.Bold,
color = if (isSystemInDarkTheme()) {
Color.Black
} else {
Color.White
}
)
}
}
}
}
}

Achieve Vertical Grid View in Jetpack Compose

My Code :::
#Composable
fun VerticalImageCards(toolsList: List<HealthTool>) {
val domainName = stringResource(id = R.string.media_url)
FlowRow(mainAxisSpacing = 8.dp,
crossAxisSpacing = 16.dp,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
mainAxisSize = SizeMode.Wrap) {
for (i in toolsList) {
val imgUrl = "$domainName${i.url}"
ToolsCardLayout(cardTitle = i.title, imgUrl = imgUrl)
}
}
}
#Composable
fun ToolsCardLayout(
imgUrl: String,
cardTitle: String,
) {
Box(modifier = Modifier.clickable {}) {
AsyncImage(model = imgUrl,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxWidth())
Box(modifier = Modifier
.size(32.dp)
.align(Alignment.TopEnd)) {
Image(painter = painterResource(id = R.drawable.schedule),
contentDescription = null,
modifier = Modifier
.fillMaxSize()
.padding(top = 8.dp, end = 8.dp),
contentScale = ContentScale.Fit)
}
}
Card(modifier = Modifier
.height(44.dp)
.fillMaxWidth()) {
Text(text = cardTitle,
style = MaterialTheme.typography.h6,
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.clip(RoundedCornerShape(bottomStart = 8.dp, bottomEnd = 8.dp)))
}
}
Code Explanation:::
I'm getting a list of tools details which is having tool image, title, description from server.
imgURl -> url for image
cardTitle -> tool's name
Problem :::
Card is taking the whole width. Earlier, I used weight(1f) when the data was hardcoded and I don't have to retrieve it from backend. Also, I'm going to use it on the Homescreen, so the grid shouldn't be scrollable.
PLEASE VISIT THIS DRIVE LINK FOR MORE DETAILS, I have added code snippet and images + screen recording for better understanding
You can use official LazyVerticalGrid
#Composable
fun VerticalImageCards(toolsList: List<HealthTool>) {
val domainName = stringResource(id = R.string.media_url) {
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = modifier,
state = rememberLazyGridState()
) {
items(toolsList) {
val imgUrl = "$domainName${it.url}"
ToolsCardLayout(cardTitle = it.title, imgUrl = imageUrl)
}
}
}

Align composable to bottom of another composable in Jetpack compose?

I have 3 composable in Column and I want to align 2nd composable to bottom of 1st composable like this:
But currently i'm getting output like this:
Here is my current code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposePlaygroundTheme {
Column(modifier = Modifier
.fillMaxSize()) {
topControls("Game 1",34,Modifier.weight(1f))
centerControls()
bottomControls()
}
}
}
}
}
#Composable
fun topControls(gamename: String, totalTime: Int, modifier: Modifier) {
Column(modifier = modifier
.background(Color.LightGray)
.padding(start = 24.dp, end = 24.dp, top = 24.dp)) {
Row(modifier = Modifier.padding(end = 32.dp)) {
Image(
painter = painterResource(R.drawable.ic_launcher_foreground),
contentDescription = "",
Modifier
.height(32.dp)
.width(32.dp)
)
Text(
text = gamename,
modifier = Modifier
.weight(1f)
.align(Alignment.CenterVertically),
textAlign = TextAlign.Center
)
}
Text(text = totalTime.toString(),modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center,fontSize = 53.sp)
Image(
painter = painterResource(R.drawable.ic_launcher_background),
contentDescription = "",
Modifier.fillMaxSize(),
contentScale = ContentScale.FillBounds
)
}
}
#Composable
fun centerControls() {
Box(modifier = Modifier
.fillMaxWidth()) {
Divider(color = Color.Blue, thickness = 1.dp, modifier = Modifier.align(Alignment.Center))
Row(modifier = Modifier.padding(start = 20.dp, end = 20.dp)) {
Button(onClick = { },colors = ButtonDefaults.buttonColors(backgroundColor = Color.Black,contentColor = Color.White), modifier = Modifier
.weight(1f)
.padding(end = 20.dp)
.fillMaxWidth()) {
Text(text = "End Game",modifier = Modifier
.wrapContentWidth()
.wrapContentHeight(), textAlign = TextAlign.Center,fontSize = 20.sp)
}
Image(painter = ColorPainter(Color.Gray), contentDescription = "", modifier = Modifier
.padding(horizontal = 25.dp)
.align(Alignment.CenterVertically)
.height(32.dp)
.width(32.dp))
}
}
}
#Composable
fun bottomControls() {
Spacer(modifier = Modifier.height(170.dp))
}
Is there anyway I can achieve this in jetpack compose without using Constraint layout?
As you need to place one view under an other one, you should use Box.
Using Modifier.align, you can specify position of your centerControls, in this case Alignment.BottomCenter.
Using Modifier.layout you can add offset, equal to half view size.
Column(modifier = Modifier
.fillMaxSize()
) {
Box(Modifier.weight(1f)) {
topControls("Game 1", 34, Modifier.fillMaxHeight())
centerControls(
Modifier
.align(Alignment.BottomCenter)
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
placeable.place(0, placeable.height / 2)
}
}
)
}
bottomControls()
}
Result:
Something like this should work
ComposePlaygroundTheme {
Column(modifier = Modifier
.fillMaxSize()) {
topControls("Game 1",34)
Box(modifier = Modifier.weight(1f)) { // FrameLayout, weight will occupy rest of the content
centerControls()
bottomControls() // will be stacked up on top of topControls()
}
}
}

Svelte lag while dragging two elements from two different components at the same time

I’ve been trying to build a simple svg editor to try out svelte. Its been going great until I built a select box for the element to drag along with the active selected element.
While dragging that selected element the selection box lags behind the element itself. I’m not sure whats wrong.
I’ve tried a few things like using the store to pass location data and putting events on a parent element so everything calculates on the same component in case that might be the issue but still it doesn’t work. I’m not sure what I’m doing wrong.
I’ve been trying to figure this out for a while but don’t have any idea what might be the issue.
You can check my codesandbox simplified demo of it here:
codesandbox.io
<script lang="ts">
import ImageViewer from "../ImageViewer/ImageViewer.svelte";
import EditorControls from "../EditorControls/EditorControls.svelte";
import { app_data, app_state } from "../../stores";
import {
getBoundingBox,
convertGlobalToLocalPosition,
} from "../../helpers/svg";
import { elementData, elementDataRect } from "../../helpers/variables";
import { mousePointerLocation } from "../../helpers/mouse";
let activeElement = {
i: 0,
bbox: {
x: 0,
y: 0,
width: 0,
height: 0,
},
active: false,
};
let elements = [{
type: 'rect',
x: 100,
y: 100,
width: 400,
height: 280,
active: true,
fill: 'rgba(0, 0, 0, 1)',
stroke: 'rgba(0, 0, 0, 1)'
}];
let app_data_value;
const unsub_app_data = app_data.subscribe((value) => {
app_data_value = value;
});
let pos = elementData;
let posRect = elementDataRect;
let strokeWidth = 15;
let app_state_value;
const unsub_app_state = app_state.subscribe((value) => {
app_state_value = { ...value };
});
let moving = app_state_value.action === "move" ? true : false;
let movePos;
let active = false;
const elementMoveDownHandler = (e) => {
if (
(e.button === 0 || e.button === -1) &&
app_data_value.tool.selected === "select"
) {
active = true;
let i = (e.target as SVGElement).getAttribute("data-obj-id");
if (!moving) {
e.target.setPointerCapture(e.pointerId);
let cursorpt: any = mousePointerLocation(e);
let bbox: any;
bbox = getBoundingBox(e.target);
const offset = {
x: e.clientX - bbox.left,
y: e.clientY - bbox.top,
};
movePos = {
...movePos,
init_x: cursorpt.x,
init_y: cursorpt.y,
offset: {
x: offset.x,
y: offset.y,
},
type: app_data_value.tool.selected,
};
let pt = convertGlobalToLocalPosition(e.target);
activeElement = {
...activeElement,
i: parseInt(i),
bbox: {
x: pt.x,
y: pt.y,
width: bbox.width,
height: bbox.height,
},
active: true,
};
moving = true;
}
}
};
const elementMoveMoveHandler = (e) => {
if (
e.button === 0 ||
(e.button === -1 && app_data_value.tool.selected === "select")
) {
active = true;
let i = (e.target as SVGElement).getAttribute("data-obj-id");
if (moving) {
let cursorpt: any;
let bbox: any;
bbox = getBoundingBox(e.target);
cursorpt = mousePointerLocation(e);
const offset = {
x: e.clientX - bbox.left,
y: e.clientY - bbox.top,
};
let j;
switch (e.target.nodeName) {
case "rect":
j = [...elements]
j[i]["x"] =
elements[i]["x"] - (movePos.offset.x - offset.x);
j[i]["y"] =
elements[i]["y"] - (movePos.offset.y - offset.y);
elements = j;
break;
default:
break;
}
// elements = elements;
movePos = {
...movePos,
move_x: cursorpt.x,
move_y: cursorpt.y,
type: app_data_value.tool.selected,
};
let pt = convertGlobalToLocalPosition(e.target);
activeElement = {
...activeElement,
bbox: {
x: pt.x,
y: pt.y,
width: bbox.width,
height: bbox.height,
},
};
// activeElement = activeElement;
app_state.update((j) => {
j.action = "move";
return j;
});
}
}
};
const elementMoveUpHandler = (e) => {
moving = false;
app_state.update((j) => {
j.action = "standby";
return j;
});
e.target.releasePointerCapture(e.pointerId);
};
</script>
<div
on:pointerdown={(e) => {
elementMoveDownHandler(e);
}}
on:pointerup={(e) => {
if (active) {
elementMoveUpHandler(e);
}
}}
on:pointermove={(e) => {
if (active) {
elementMoveMoveHandler(e);
}
}}
>
<ImageViewer {strokeWidth} {elements} />
<EditorControls {pos} {posRect} {activeElement} />
</div>
<style lang="scss">
#import "./SVGEditor.scss";
</style>
<script lang="">
import { app_data } from "../../stores";
export let strokeWidth;
export let elements;
let app_data_value;
const unsub_app_data = app_data.subscribe((value) => {
app_data_value = value;
});
</script>
<svg
id="image-viewer"
width={app_data_value.doc_size.width}
height={app_data_value.doc_size.height}
>
{#each elements as item, i}
{#if typeof item === "object"}
{#if "type" in item}
{#if item.type === "line"}
{#if "x1" in item && "y1" in item && "x2" in item && "y2" in item}
<line
x1={item.x1}
y1={item.y1}
x2={item.x2}
y2={item.y2}
stroke="black"
stroke-width={strokeWidth}
data-obj-id={i}
/>
{/if}
{/if}
{#if item.type === "rect"}
{#if "x" in item && "y" in item && "width" in item && "height" in item}
<rect
x={item.x}
y={item.y}
width={item.width}
height={item.height}
stroke="black"
stroke-width={strokeWidth}
data-obj-id={i}
/>
{/if}
{/if}
{/if}
{/if}
{/each}
</svg>
<style lang="scss">
#import "./ImageViewer.scss";
</style>
<script lang="ts">
import { app_data } from "../../stores";
import SelectCtrl from "../SelectCtrl/SelectCtrl.svelte";
export let pos;
export let posRect;
export let activeElement;
let app_data_value;
const unsub_app_data = app_data.subscribe((value) => {
app_data_value = value;
});
let active;
$: active = activeElement.active;
let strokeWidth = 2;
</script>
<svg
id="editor-controls"
width={app_data_value.doc_size.width}
height={app_data_value.doc_size.height}
>
{#if active}
<SelectCtrl activeElement={activeElement} strokeWidth={2}/>
{/if}
</svg>
<style lang="scss">
#import "./EditorControls.scss";
</style>
<script lang="typescript">
export let activeElement;
export let strokeWidth;
let x = 0;
let y = 0;
let width = 0;
let height = 0;
$: x = activeElement.bbox.x;
$: y = activeElement.bbox.y;
$: width = activeElement.bbox.width;
$: height = activeElement.bbox.height;
let fill = 'rgba(0,0,0,0)';
let stroke = '#246bf0';
let strokeWidthMain;
$: strokeWidthMain = strokeWidth*2;
</script>
<g
class="selector-parent-group"
>
<g
class="selection-box"
>
<rect
class="bounding-box"
x={x}
y={y}
width={width}
height={height}
fill={fill}
stroke={stroke}
stroke-width={strokeWidthMain}
/>
<rect
class="bounding-box-light"
x={x-strokeWidthMain}
y={y-strokeWidthMain}
width={width+strokeWidthMain*2}
height={height+strokeWidthMain*2}
fill={fill}
stroke={'#B9B9B9'}
stroke-width={strokeWidthMain}
/>
</g>
</g>
Edit: I didn't think to add the code for the convertGlobalToLocalPosition and getBoundingBox functions but thanks to the answer that solved my problem it would better illustrate the problem I had if I add that code as well.
export function convertGlobalToLocalPosition(element: any) {
if (!element) return { x: 0, y: 0 };
if (typeof element.ownerSVGElement === 'undefined') return { x: 0, y: 0 };
var svg = element.ownerSVGElement;
// Get the cx and cy coordinates
var pt = svg.createSVGPoint();
let boxParent = getBoundingBox(svg);
let box = getBoundingBox(element);
pt.x = box.x - boxParent.x;
pt.y = box.y - boxParent.y;
while (true) {
// Get this elementents transform
var transform = element.transform.baseVal.consolidate();
// If it has a transform, then apply it to our point
if (transform) {
var matrix = element.transform.baseVal.consolidate().matrix;
pt = pt.matrixTransform(matrix);
}
// If this elementent's parent is the root SVG elementent, then stop
if (element.parentNode == svg)
break;
// Otherwise step up to the parent elementent and repeat the process
element = element.parentNode;
}
return pt;
}
export function getBoundingBox(el: any) {
let computed: any = window.getComputedStyle(el);
let strokeWidthCalc: string = computed['stroke-width'];
let strokeWidth: number = 0;
if (strokeWidthCalc.includes('px')) {
strokeWidth = parseFloat(strokeWidthCalc.substring(0, strokeWidthCalc.length - 2));
} else {
// Examine value further
}
let boundingClientRect = el.getBoundingClientRect();
let bBox = el.getBBox();
if (boundingClientRect.width === bBox.width) {
boundingClientRect.x -= strokeWidth / 2;
boundingClientRect.y -= strokeWidth / 2;
boundingClientRect.width += strokeWidth;
boundingClientRect.height += strokeWidth;
}
return boundingClientRect;
}
I think this is causing your issue:
In elementMoveMoveHandler you are updating the element's position here:
j = [...elements];
j[i]["x"] = elements[i]["x"] - (movePos.offset.x - offset.x);
j[i]["y"] = elements[i]["y"] - (movePos.offset.y - offset.y);
elements = j;
and right after that you are reading the position from the DOM in the convertGlobalToLocalPosition function. Svelte will batch DOM updates and it did not have time to update the DOM element. Therefore convertGlobalToLocalPosition will give you an old value. The easiest fix would be to add await tick(); right before let pt = convertGlobalToLocalPosition(e.target); and make elementMoveMoveHandler async.
You can read more about the tick function here: https://svelte.dev/tutorial/tick
A few more suggestions:
You don't need to manually subscribe to stores or call the update function. You can use the $-symbol and Svelte will take care of subscribing, unsubscribing and notifying subscribers for you. https://svelte.dev/tutorial/auto-subscriptions
You can certainly use immutability in Svelte, but it is not needed. Personally, I find mutable code more readable.

Can't I use await animation on Device.StartTimer()?

I'm making app with Xamarin.forms pcl.
If I use UIRefreshControl with Scrollview on iOS and animation same time.
It crash!
Is this bug of UIRefreshControl?
I provide reproduce sample code here.
https://github.com/myallb/test_pulltorefresh.git
Sample code is very simple.
Is there any mistake??
Thanks.
public partial class testPage : ContentPage
{
public PullToRefreshLayout RefreshView = null;
AbsoluteLayout layout;
public testPage()
{
InitializeComponent();
layout = new AbsoluteLayout()
{
BackgroundColor = Color.Purple,
};
ScrollView scrollview = new ScrollView()
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Content = layout
};
RefreshView = new PullToRefreshLayout
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Content = scrollview,
RefreshColor = Color.Red,
RefreshCommand = new Command(RefreshStart)
};
RefreshView.IsPullToRefreshEnabled = true;
Content = RefreshView;
Device.StartTimer(new TimeSpan(0, 0, 1), ani);
}
bool ani()
{
Label z = new Label()
{
Text = "Z",
TextColor = Color.White,
FontAttributes = FontAttributes.Bold,
FontSize = new Random().Next(22, 35)
};
AbsoluteLayout.SetLayoutBounds(z, new Rectangle(0.67 + new Random().Next(0, 10) / 100.0, 0.13 + new Random().Next(0, 10) / 100.0, 40, 40));
AbsoluteLayout.SetLayoutFlags(z, AbsoluteLayoutFlags.PositionProportional);
layout.Children.Add(z);
Device.BeginInvokeOnMainThread(async () =>
{
Task t1 = z.FadeTo(0, 3500);
Task t2 = z.TranslateTo(0, -70, 3500, Easing.SinInOut);
await Task.WhenAll(t1, t2);
layout.Children.Remove(z);
});
return true;
}
void RefreshStart()
{
Debug.WriteLine("RefreshStart");
if (RefreshView != null)
RefreshView.IsRefreshing = true;
Device.BeginInvokeOnMainThread(async () =>
{
await Task.Delay(20);
Debug.WriteLine("RefreshEnd");
RefreshView.IsRefreshing = false;
});
}
}

Resources