How does this function keep track of clicks? - android-jetpack-compose

On the "Thinking in Compose" page I don't get this code, how does $clicks keep track of number of clicks?
#Composable
fun ClickCounter(clicks: Int, onClick: () -> Unit) {
Button(onClick = onClick) {
Text("I've been clicked $clicks times")
}
}
I'm learning Kotlin at the same time as Compose, so I get puzzled all the time.

It's omitted in that example but it should store the click-count in a MutableState<T> wrapped with remember
var clickCount by remember { mutableStateOf(0)}
ClickCounter(clicks = clickCount, onClick = {clickCount += it})

For real real real beginner like me, let me add my comment and code.
When the ClickCounter composable is called we need to pass two parameters.
To 'clicks', we pass initial value 0 which will be changed everytime we click the button. Also the initial value need to be 'remembered' and tracked for further changes. So we create new variable using 'mutableStateOf' function inside 'remember' function.
To 'onClick', we need to pass function which adds 1 everytime the button is clicked.
As a caller of the ClickCounter composable, I made a simple preview composable as below.
#Preview(showBackground = true)
#Composable
fun ClickCounterPreview(){
var clicks by remember { mutableStateOf(0) }
// We call ClickCounter composable passing two parameters.
ClickCounter(clicks = clicks, onClick = { clicks += 1 })
}

Related

How to get initial focus in compose

How to get initial keyboard focus on an Android compose app?
My view looks like
Parent { Child { Button} }
I tried implementing it in the Parent composable function....
FocusRequester is not initialized. Here are some possible fixes:
1. Remember the FocusRequester: val focusRequester = remember { FocusRequester() }
2. Did you forget to add a Modifier.focusRequester() ?
3. Are you attempting to request focus during composition? Focus requests should be made in response to some event. Eg Modifier.clickable { focusRequester.requestFocus() }
The following code woirks like a charm....
fun Modifier.requestInitialFocus() = composed {
val first = remember { FocusRequester() }
LaunchedEffect(first) {
delay(1)
first.requestFocus()
}
focusRequester(first)
}
Original:
This error does not happen when implementing it in the composable function, where the target element is a direct child....
So implementing it in the Child seems to be a solution....

rac_command only responds to single click

I am trying to detect clicks on an UIElement like a button using Reactive Cocoa, using RAC for the first time in MVVM architecture.
I set the rac_command for my button in my ViewController.
addContactBtn.rac_command = viewModel.addContact
My ViewModel does the following:
func init(){
self.addContact = RACCommand() {
(any:AnyObject!) -> RACSignal in
return RACSignal.createSignal({
(subscriber: RACSubscriber!) -> RACDisposable! in
print("creating viewModel")
let viewModel = ContactAddViewModel(services: self.services)
self.services.pushViewModel(viewModel)
return RACDisposable(block: {
})
})
}
}
However, the command is executed only once and then the button is in disabled state when I pop the view controller and come back to original viewController. How can detect the button click any number of times?
Obviously, you missed something and had a simple mistake.
RACCommand expects to a signal which will be alive when the button clicked and be disposed after click-business-logic (like create viewModel, then pushViewModel in the above) executed. That is to say each button clicking-event associates a signal, not shares one unique signal, but has common signal inner logic. If a signal does not achieve completion or error, the responding clicking-event is not finished such that the button is disabled.
The below modified codes could be correct.
func init(){
self.addContact = RACCommand() {
(any:AnyObject!) -> RACSignal in
return RACSignal.createSignal({
(subscriber: RACSubscriber!) -> RACDisposable! in
print("creating viewModel")
let viewModel = ContactAddViewModel(services: self.services)
self.services.pushViewModel(viewModel)
// if you like to expose some value
// subscriber.sendNext(value)
subscriber.sendCompleted() // which makes clicking-event finished
// maybe error occurs
// subscriber.sendError()
return RACDisposable(block: {
})
})
}
}
I would like to advise you to checkout CocoaAction and Action in ReactiveSwift, which are replacement for RACCommand of legacy ReactiveObjC.

Flipswitch in lightswitch is going in an infinite loop

I got this piece of code for rendering and using Flipswitch as a custom control in lightswitch application.
function createBooleanSwitch(element, contentItem, trueText, falseText, optionalWidth) {
var $defaultWidth = '5.4em';
var $defaultFalseText = 'False';
var $defaultTrueText = 'False';
var $selectElement = $('<select data-role="slider"></select>').appendTo($(element));
if (falseText != null) {
$('<option value="false">' + falseText + '</option>').appendTo($selectElement);
}
else {
$('<option value="false">' + $defaultFalseText + '</option>').appendTo($selectElement);
}
if (trueText != null) {
$('<option value="true">' + trueText + '</option>').appendTo($selectElement);
}
else {
$('<option value="true">' + $defaultTrueText + '</option>').appendTo($selectElement);
}
// Now, after jQueryMobile has had a chance to process the
// new DOM addition, perform our own post-processing:
$(element).one('slideinit', function () {
var $flipSwitch = $('select', $(element));
// Set the initial value (using helper function below):
setFlipSwitchValue(contentItem.value);
// If the content item changes (perhaps due to another control being
// bound to the same content item, or if a change occurs programmatically),
// update the visual representation of the control:
contentItem.dataBind('value', setFlipSwitchValue);
// Conversely, whenver the user adjusts the flip-switch visually,
// update the underlying content item:
$flipSwitch.change(function () {
contentItem.value = ($flipSwitch.val() === 'true');
});
// To set the width of the slider to something different than the default,
// need to adjust the *generated* div that gets created right next to
// the original select element. DOM Explorer (F12 tools) is a big help here.
if (optionalWidth != null) {
$('.ui-slider-switch', $(element)).css('width', optionalWidth);
}
else {
$('.ui-slider-switch', $(element)).css('width', defaultWidth);
}
//===============================================================//
// Helper function to set the value of the flip-switch
// (used both during initialization, and for data-binding)
function setFlipSwitchValue(value) {
$flipSwitch.val((value) ? 'true' : 'false');
// Having updated the DOM value, refresh the visual representation as well
// (required for a slider control, as per jQueryMobile's documentation)
$flipSwitch.slider(); // Initializes the slider
$flipSwitch.slider('refresh');
// Because the flip switch has no concept of a "null" value
// (or anything other than true/false), ensure that the
// contentItem's value is in sync with the visual representation
contentItem.value = ($flipSwitch.val() === 'true');
}
});
}
This piece of code works fine. It renders the flipswitch on the screen. I am showing the data in an Edit screen, which is coming in a popup. Problem arises when I open that popup which contains the flipswitch and without changing any data on UI, I just try to close that popup screen. The IE hangs and it gives error saying that long script is running. When I debug the createBoolenaSwitch function, I came to know that it is going in infinite loop inside the function called setFlipSwitchValue(value){}
Why is this function getting called and this is going in an infinite loop?

Double tap using Appium Java 2.0

I couldn't find an implementation of a double tap for Appium that was straightforward and allowed you to pass in the element locator strategy, so here goes:
public static void doubleTapElementBy(By by) {
WebElement el = getDriver().findElement(by);
MultiTouchAction multiTouch = new MultiTouchAction(getDriver());
TouchAction action0 = new TouchAction(getDriver()).tap(el).waitAction(50).tap(el);
try {
multiTouch.add(action0).perform();
} catch (WebDriverException e) {
logger.info("Unable to do second tap on element, probably because element requieres single tap on this Android version");
}
}
You can also try below approach using tap method in TouchAction class.
TouchAction taction = new TouchAction(driver);
taction.tap(tapOptions().withElement(ElementOption.element(YOUR_WebElement))
.withTapsCount(2)).perform();
You will need to add below static import as well:
import static io.appium.java_client.touch.TapOptions.tapOptions;
This is a workaround in pseudocode and possibly there's a more "official" way to do it, but it should do the work if no other solution is available:
Interpretmessages(){
switch(msg)
{
OnClick:
{ if (lastClicked - thisTime() < 0.2) //if it was clicked very recently
{doubleTapped()} //handle it as a double tap
else{lastClicked = thisTime()} //otherwise keep the time of the tap
} //end of OnClick
} //End of Message Handler
}//End of switch
}//End of messageHandler
If you have access to ready timer functions, you can set a function to be executed 0.2s after the click has gone off:
OnClick: if (!functionWaiting) // has the timer not been set?
{
enableTimer(); // set a function to go off in x time
clicks = 0; //we'll tell it that there's been one click in a couple of lines
} //set it for the first click
clicks++; //if it's already clicked, it'll become 2 (double tap) otherwise it's just one
So, the idea is that when you get a tap, you check if there's been another one recently (a. by checking the relative times, b. by checking if the function is still pending) and you handle it dependingly, only note that you will have to implement a timer so your function fires a bit later so you have time to get a second tap
The style draws upon the Win32's message handling, I'm pretty sure it works there, it should work for you too.
Double tap and hold -- Use below code:
new TouchAction(driver).press(112,567).release().perform().press(112,567).perform();
Double tap -- Use below code:
new TouchAction(driver).press(112,567).release().perform().press(112,567).release().perform();

Anonymous callback function to JsObject doesn't update Dart Controller until after a user action

Given the dart code
class LandingController {
bool hideDiv = false;
void doit() {
new JsObject(context['loginControls']).callMethod('fadeLogin', [() {
print(hideDiv);
hideDiv = true;
print(hideDiv);
print("WTF");
}]);
}
}
which calls the JS:
var loginControls = function() {
this.fadeLogin = function(func) {
$('#landingHeader').animate({ opacity: 0 }, 500, func);
}
};
which should affect the view:
<button ng-click="doit();">Click</button>
<div id="landingHeader">Hide me after button click</div>
<div ng-if="ctrl.hideDiv == false"><img src="assets/img/ajax-loader-small.gif">Waiting for div to disappear...</div>
After a button click and a 500 millisecond delay I see in my console a "WTF" print correctly. The div, however, is still visible. Once a user action occurs, in my case a mouse click, the div magically disappears. It seems as though the controller's value is changed, but the browser doesn't receive the change to hide the div, as I've printed the controller's value in the anonymous callback.
There is a work around, but it involves setting Dart timer's to the same fade times that you use in the javascript after the JsObject call and setting your controller's values in those Timer callbacks - gross but it works.
I think you need to call scope.apply(). I think Angular just can't recognize the value change in hideDiv when doit() is called from another zone (like JS).
You usually don't need to call scope.apply() in Angular.dart but I think this is one of the exceptions.
Is the animate function the only reason you use jQuery? It might be easier to do this with Angular.darts animation features.

Resources