Any ways to detect Cocos2d-x: CCScrollView is scrolling or not? - cocos2d-x

I'm seeking a way to get the scrolling state of CCScrollView.
It seems not to be a rare requirement, but do I need to implement it?
Thanks:)
Edit: Following is my two 'brute force' ways, but they seems work.
The goal is to get the scroll condition of a CCScrollView s from a cocos2d::Layer l.
Way #1
In each iteration of update() function of l, get the content offset of s by
ScrollView::getContentOffset()
if it stays the same, we can assume that the ScrollView is not scrolling.
Way #2
Create class S which inherits CCScrollView and CCScrollViewDelegate, then in the override of the delegate's function
void scrollViewDidScroll(ScrollView* view)
(which seems to be called every time the ScrollView scrolls.) use a variable to save current time
/*uint64_t*/ lastScrollTime = mach_absolute_time();
then in the update() function of l, assume the ScrollView is not scrolling by a time threshold
curTime = mach_absolute_time();
if (GlobalUtils::machTimeToSecs(curTime - lastScrollTime) > 0.1)
Hope that works :)

So, in your's initScroll() method you should set:
scrollView = ScrollView::create();
// initialisation scrollview
scrollView->addEventListener(CC_CALLBACK_2(YourScene::testScroll, this));
this->addChild(scrollView);
and make bool variable isScrolled. Then in method testScroll() you need to check listener's event type and depending from it set the variable isScrolled:
void YourScene::testScroll(Ref* sender, ui::ScrollView::EventType type)
{
if (type == ui::ScrollView::EventType::SCROLLING)
isScrolling = true;
else
isScrolling = false;
}
You can see other values EventType here

One way without modifying the source code of CCScrollView is to check the flag of ScrollView::isDragging() and whether one of the scrolling selectors of ScrollView is scheduled.
bool isDeaccelerateScrolling = scrollView->getScheduler()->isScheduled(CC_SCHEDULE_SELECTOR(ScrollView::deaccelerateScrolling));
bool isPerformedAnimatedScroll = scrollView->getScheduler()->isScheduled(CC_SCHEDULE_SELECTOR(ScrollView::performedAnimatedScroll));
bool isScrolling = scrollView->isDragging() || isDeaccelerateScrolling || isPerformedAnimatedScroll;

Related

ScrollViewer ChangeView - AccessViolationException one out of the thousand times

I have FlipView (it's virtualized with Mode="Standard") with ScrollViewer, Image (inside ScrollViewer as DataTemplate). I set ImageOpened event handler on Image with such code:
private void Image_ImageOpened(object sender, RoutedEventArgs e)
{
var image = sender as Image;
double width = image.ActualWidth;
double height = image.ActualHeight;
var sv = image.Parent as ScrollViewer;
if (sv != null && width != 0 && height != 0)
{
var properZoom = ZoomUtilities.CalculateZoomFactor(width, height);
sv.MinZoomFactor = 0.3f;
sv.ChangeView(null, null, properZoom);
sv.MinZoomFactor = properZoom;
}
}
properZoom is always correct value. One out of the thousand times when I change item (swipe) or load page with this FlipView application crash with breakpoint on sv.ChangeView(..) and AccessViolationException exception is thrown. Does anyone know what could be the reason of such behaviour? Are there any restriction when I can call ChangeView method?
EDIT:
Forgot to mention - there is also DoubleTapped event handler on ScrollViewer which also calls ChangeView
I had the same AccessViolationException using .ChangeView(..). I noticed the exception was thrown when the ScrollViewer I wanted to manipulate had focus. For example when a user was dragging through the list or controlling the scrollbar with his mouse.
Disabling animations solved my problems. If you want to keep the animations you should look for a way to remove focus on the controls before changing the view. I guess the animated ChangeView tries to change a property that is locked because of the user input.
Hope this helps.
I had the same problem.
Solution to me: call inverse, from image object to parent got error.
Call normal path: objectscroolviewer.changeview, works no error.
Then:
var sv = image.Parent as ScrollViewer;
change to
var sv = ScrollViewerObject;
Get object to variable externally, not by internal reference of parent, and ignores sender parameter.

Input detection for a Group with overlapping transparent Images

I use a Group to store some Images and draw them to a SpriteBatch. Now I want to detect which Image has been clicked. For this reason I add an InputListener to the Group to get an event on touch down. The incoming InputEvent got an method (getTarget) that returns an reference to the clicked Actor.
If I click on a transparent area of an Actor I want to ignore the incoming event. And if there is an Actor behind it I want to use this instead. I thought about something like this:
myGroup.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Actor targetActor = event.getTarget();
// is the touched pixel transparent: return false
// else: use targetActor to handle the event and return true
};
});
Is this the right way to do it? I thought returning false for the method touchDown would continue propagation for the event and let me also receive touchDown events for other Actors at the same position. But this seems to be a misunderstanding...
UPDATE
P.T.s answer solves the problem of getting the right Event. Now I have got the problem to detect if the hit pixel is transparent. It seems to me that I need the Image as a Pixmap to get access. But I do not know how to convert an Image to a Pixmap. I also wonder if this is a good solution in terms of performance and memory usage..
I think you want to override the Actor.hit() method. See the scene2d Hit Detection wiki.
To do this, subclass Image, and put your hit specialization in there. Something like:
public Actor hit(float x, float y, boolean touchable) {
Actor result = super.hit(x, y, touchable);
if (result != null) { // x,y is within bounding box of this Actor
// Test if actor is really hit (do nothing) or it was missed (set result = null)
}
return result;
}
I believe you cannot accomplish this within the touchDown listener because the Stage will have skipped the Actors "behind" this one (only "parent" actors will get the touchDown event if you return false here).

AS3 Profiling - Why do Object and Function counts increase when setting text

Firstly can some one please explain what is meant by Object and Function in a profiling environment.
Secondly, why does the Object and Function count increase when I repeatedly set the text property of a textfield:
override public function setLanguage(id:String):void
{
if (id == "en")
{
ui.title.text = _data.text.title.en;
ui.title.direction = Direction.LTR;
}
else if (id == "ae")
{
ui.title.text = _data.text.title.en;
ui.title.direction = Direction.RTL;
}
}
From Laurent:
Internally, TextField::text is most likely a getter/setter (since it needs to set a flag to update the text field display, also possibly update the HTML content, etc.) so when you set it you are effectively calling a function.
This means that TextField.text is implemented as a property getter and setter, so if you had to code it, you would see something like
private var _text:String="";
public function get text():String {
return _text;
}
public function set text(value:String):void {
_text=value;
}
Your Object count increases every time you reference (looking for a better word, don't kill me about this :P) an object (I trust you know what objects are), and your Function count increases every time you invoke a function.
So when you do something like
myTextField.text="Hello World";
you are referencing object myTextField and invoking its function set text(String);, causing your counts to increase by 1 each.
Internally, TextField::text is most likely a getter/setter (since it needs to set a flag to update the text field display, also possibly update the HTML content, etc.) so when you set it you are effectively calling a function.
What is it you don't understand about the difference between a Function and an Object? Could you be more specific?

AS3: How to know if dataprovider or its content(s) is changed

I'm implementing some kind of combobox control (by extending spark.components.supportClasses.DropDownListBase)
Now, inside this control; I need to know:
if the dataprovider is changed/assigned. (which I can do... the first approach below works);
if any item in the dataprovider collection has changed.
I tried 2 methods that did not do the trick...
1ST APPROACH:
[Bindable("collectionChange")]
override public function set dataProvider(value:IList):void
{
if (value) value.addEventListener(CollectionEvent.COLLECTION_CHANGE, onDataChange);
super.dataProvider = value;
trace("DATA CHANGED"); //fires
}
protected function onDataChange(event:CollectionEvent):void
{
trace("COLLECTION ITEM(S) CHANGED"); //does not fire
}
2ND APPROACH:
Since this is based on DropDownListBase; it should dispatch the CollectionEvent.COLLECTION_CHANGE event already..?
public function myClass() //constructor
{
addEventListener(CollectionEvent.COLLECTION_CHANGE, onDataChange);
}
protected function onDataChange(event:CollectionEvent):void
{
trace("DATA CHANGED"); //does not fire
}
Any ideas?
UPDATE: Edited above.. The first approach lets me know if the dataprovider is changed but not if any item is updated in the dataprovider collection. The second approach does not work at all..
We'll be able to help significantly more if you show us a runnable sample to demonstrate the problem.
if the dataprovider is
changed/assigned.
Your first approach should work. Can you tell us what makes you think it didn't? ( No trace statement I assume? ). And tell us what you did to change the dataProvider.
The second approach won't work because myClass is not firing off the collectionChange event.
2 if any item in the dataprovider collection has changed.
There is not really a way to tell this. In most cases, a collection is just a list of pointers to other objects. If you change those pointers, then a collectionChange event is fired. IF you change the item that he pointer is pointed to, the collection has no way to know that something changed. Binding works very similarly if your an MXML fan.
If you have control over how items are changed, you can deal with it that way. Instead of:
(collection.getITemAt(x) as myObject).property = newValue;
Do something like this:
var myObject : MyObject = collection.getITemAt(x) as myObject
myObject.property = newValue;
collection.setItemAt(x, myObject);
I would expect that to fire a collectionChange event, but not the former.
That said, in the context of a dropDownListBase: As you scroll or open and close the drop down, the itemRenderers should be updated to reflect the most current dataProvider's data. But if you change something on the fly while the drop down is open, I would not expect it to update automatically [if you're not changing the dataProvider.

DependencyProperty PropertyChangedCallback causes NullReferenceException in XAML

I've got a subclassed UserControl that is the content for my main window. I added a DepedencyProperty to my usercontrol, of type ResizeMode, and the PropertyChanged callback sets the ResizeMode of the main window to the value correctly. It runs fine. I can set it from the code behind, or from the XAML, and it works correctly.
However, when I set it from XAML, the designer throws an Object reference not set to an instance of an object exception, on the code in the PropertyChanged callback that sets the window's resize.
<classes:MyUserControl ResizeMode="NoResize">
<...>
</classes:MyUserControl>
This is the callback. MainWindow is a reference to the parent window.
private static void OnResizeModeChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
(o as MyUserControl).MainWindow.ResizeMode = (ResizeMode) e.NewValue;
}
public ResizeMode ResizeMode
{
get { return (ResizeMode) GetValue(ResizeModeProperty); }
set { SetValue(ResizeModeProperty, value); }
}
public static readonly DependencyProperty ResizeModeProperty =
DependencyProperty.Register("SizeToFit", typeof(ResizeMode), typeof(MyUserControl),
new UIPropertyMetadata(ResizeMode.CanResize, new PropertyChangedCallback(OnResizeModeChanged)));
I could ignore it, or set it in the code behind, but I don't really understand the reason for this error, and I would prefer to set it in XAML.
Can anyone shed some light?
Do you know exactly where the NullReferenceExceptoin is being thrown? For example, if you try this instead:
var uc = o as MyUserControl;
var mw = uc.MainWindow;
mw.ResizeMode = (ResizeMode)e.NewValue;
... then is the exception raised on the second line or the third?
My feeling is that MainWindow has not been assigned by the time ResizeMode is first given a value, so accessing MainWindow.ResizeMode is causing the error.
If that's the case, it's safe to ignore:
var mw = (o as MyUserControl).MainWindow;
if (mw == null) return;
But you might want to cache the value somewhere, and then assign it to MainWindow.ResizeMode when MainWindow gets assigned later.
OK, I think I found the culprit.
The MainWindow is set by App.Current.MainWindow.
Now from what I've read, the Current.MainWindow doesn't exist in Design time, and then when the OnResizeModeChanged methods fire during designtime, MainWindow.ResizeMode, boom! Exception!
I added this line to my methods:
if ((bool) (DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue)) return;
I encountered another problem where my XAML suddenly couldn't load my usercontrol, due to me setting some properties on MainWindow in the constructor, added this:
if (DesignerProperties.GetIsInDesignMode(this))
return;