CCScrollView with CCControlButton. How to control the touch area? - cocos2d-x

I have CCScrollView with container with CCControlButtons, when the buttons scroll out of the visible CCScrollView area, They are also can be touched too. How can I control the area ?

Inspired by Tomasz's answer, I created an alternative solution, also inheriting from CCControlButton:
bool ScrollableButton::isTouchInside(CCTouch *touch) {
return !dragging && CCControlButton::isTouchInside(touch);
}
bool ScrollableButton::ccTouchBegan(CCTouch *touch, CCEvent *event) {
dragging = false;
return CCControlButton::ccTouchBegan(touch, event);
}
void ScrollableButton::ccTouchMoved(CCTouch *touch, CCEvent *event) {
if (!dragging && ccpDistance(touch->getLocation(), touch->getStartLocation()) > 25) {
dragging = true;
}
CCControlButton::ccTouchMoved(touch, event);
}
void ScrollableButton::ccTouchEnded(CCTouch *touch, CCEvent *event) {
CCControlButton::ccTouchEnded(touch, event);
dragging = false;
}
void ScrollableButton::ccTouchCancelled(CCTouch *touch, CCEvent *event) {
CCControlButton::ccTouchCancelled(touch, event);
dragging = false;
}
The secret sauce is the override of the isTouchInside function, which will return false even if the touch is inside, but was moved. This way, the button will also release its "zoomed" state as soon as you start scrolling.
It also adds a small tolerance factor, so if the touch moves just a little, it's still considered a "click". This factor is hardcoded at 25 in the example above.

My problems:
There is a scrollView with many buttons (items). Above it there are 2 function buttons (return, start).
When I scroll down item buttons overlie function buttons. When I swallow all touches above my scrollview I will lose my function buttons. So I have to find another solution.
When I start draging scroll view a item button is pressed. When I ended the button action will be execute. This is very annoying.
But there is the solution. I have created new CCControlButton. It checks whether was clicked outside scrollview or was dragged. The button is used for items buttons.
bool ControlButtonForScrolling::checkIfTouchIsInsideScrollView(CCTouch *pTouch)
{
CCPoint touchLocation = pTouch->getLocation(); // Get the touch position
touchLocation = _scrollView->getParent()->convertToNodeSpace(touchLocation);
CCRect bBox=_scrollView->boundingBox();
bool result = bBox.containsPoint(touchLocation);
return result;
}
bool ControlButtonForScrolling::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
{
bool isInside = this->checkIfTouchIsInsideScrollView(pTouch);
if (isInside) {
return CCControlButton::ccTouchBegan(pTouch, pEvent);
}
else
{
return false;
}
}
void ControlButtonForScrolling::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
{
CCControlButton::ccTouchMoved(pTouch, pEvent);
_scrollWasDragged = true; // information about dragging is stored to prevent sending action
}
void ControlButtonForScrolling::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
{
// this method is a copy of CCControlButton::ccTouchEnded except lines with _scrollWasDragged
m_eState = CCControlStateNormal;
m_isPushed = false;
setHighlighted(false);
if (!_scrollWasDragged)
{
if (isTouchInside(pTouch))
{
sendActionsForControlEvents(CCControlEventTouchUpInside);
}
else
{
sendActionsForControlEvents(CCControlEventTouchUpOutside);
}
}
_scrollWasDragged = false;
}

Related

how to do a button click in Xamarin Forms Android custom info window

In my Xamarin Forms Android project I am showing a Map using CustomMapRenderer. I am popping up a Info Window in Map screen and this info window has button name called "Call". I need to do a "OnCLick" button click operation for this button. I have googled it, but unfortunately I didn't come across any solutions. at last I have started to work on converting the Android Native code -Java into Xamarin Forms Android - c#, for this conversion I have been using this answers Google Maps Android API v2 - Interactive InfoWindow (like in original android google maps) . This converted code shows A field initializer cannot reference the non static field, method or property OnInfoWindowElemTouchListener.onClickConfirmed(view, marker) this Error inside Java.Lang.Runnable please help me to resolve the problem.
OnInfoWindowElemTouchListener.cs
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Gms.Maps.Model;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Views;
using Android.Views.Accessibility;
using Java.Lang;
namespace Hotel.Droid
{
public abstract class OnInfoWindowElemTouchListener : Java.Lang.Object
, View.IOnTouchListener
{
private View view;
private Drawable bgDrawableNormal;
private Drawable bgDrawablePressed;
private Handler handler = new Handler();
private Marker marker;
private static bool endPressStatus = false;
private bool pressed = false;
public OnInfoWindowElemTouchListener(View view, Drawable bgDrawableNormal, Drawable bgDrawablePressed)
{
this.view = this.view;
this.bgDrawableNormal = this.bgDrawableNormal;
this.bgDrawablePressed = this.bgDrawablePressed;
}
public OnInfoWindowElemTouchListener()
{
}
public void setMarker(Marker marker)
{
this.marker = this.marker;
}
public bool OnTouch(View vv, MotionEvent e)
{
if (0 <= e.GetX() && e.GetX() <= vv.Width && 0 <= e.GetY() && e.GetY() <= vv.Height)
{
switch (e.ActionMasked)
{
case MotionEventActions.Down:
startPress();
break;
// We need to delay releasing of the view a little so it shows the
// pressed state on the screen
case MotionEventActions.Up:
//handler.PostDelayed(ConfirmClickRunnable, 150);
Task.Factory.StartNew(() => onClickConfirmed(view, marker));
Task.Delay(150);
break;
case MotionEventActions.Cancel:
endPress();
break;
default:
break;
}
}
else {
// If the touch goes outside of the view's area
// (like when moving finger out of the pressed button)
// just release the press
endPress();
}
return false;
}
private void startPress()
{
if (!pressed)
{
pressed = true;
//handler.RemoveCallbacks(ConfirmClickRunnable);
view.SetBackgroundDrawable(bgDrawablePressed);
if ((marker != null))
{
marker.ShowInfoWindow();
}
}
}
public bool endPress()
{
if (pressed)
{
this.pressed = false;
handler.RemoveCallbacks(ConfirmClickRunnable);
view.SetBackgroundDrawable(bgDrawableNormal);
if ((marker != null))
{
marker.ShowInfoWindow();
}
endPressStatus = true;
return true;
}
else {
endPressStatus = false;
return false;
}
}
private Runnable ConfirmClickRunnable = new Java.Lang.Runnable(() =>
{
if (endPressStatus)
{
onClickConfirmed(view, marker);
}
});
/*private class RunnableAnonymousInnerClassHelper : Java.Lang.Object, Java.Lang.IRunnable
{
private readonly Context outerInstance;
public RunnableAnonymousInnerClassHelper(Context outerInstance)
{
this.outerInstance = outerInstance;
}
public void Run()
{
if (endPressStatus)
{
onClickConfirmed();
}
}
}*/
protected abstract void onClickConfirmed(View v, Marker marker);
}
}
Updated
I have implemented the Task.Factory.StartNew instead of Android Runnableand now I am stucking on the below lines. I am struggling on converting this below Java codes into C#since it is written by Anonymous class concept.
Java
this.infoButtonListener = new OnInfoWindowElemTouchListener(infoButton,
getResources().getDrawable(R.drawable.btn_default_normal_holo_light),
getResources().getDrawable(R.drawable.btn_default_pressed_holo_light))
{
#Override
protected void onClickConfirmed(View v, Marker marker) {
// Here we can perform some action triggered after clicking the button
Toast.makeText(MainActivity.this, marker.getTitle() + "'s button clicked!", Toast.LENGTH_SHORT).show();
}
};
this.infoButton.setOnTouchListener(infoButtonListener);
infoButton in code is Call button
C# - Please help me to resolve the problem by converting/using(How to use) the above java code
The solution is too complicated for this window. Please see chat room for step by step solution.
https://chat.stackoverflow.com/rooms/128847/discussion-between-jamal-and-yuri-s

KeyUp/KeyDown is only caught once

I am trying to display a Dialog every time the keys BACK or SCAPE are pressed. However the event is only been caught once, dialog is shown but if I close it by pressing my button NO, then it will never appear again until I go to another screen.
This is how I catch the KeyUp event:
#Override
public boolean keyUp(int keycode) {
if (keycode == Keys.BACK || keycode == Keys.ESCAPE) {
dialog.setVisible(true);
}
return false;
}
This is my button inside of the Dialog:
btnNo.addListener(
new ClickListener() {
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button)
{
return true;
}
public void touchUp(InputEvent event, float x, float y, int pointer, int button){
dialog.setVisible(false);
}
});
If you have any idea ,please let me know...
Check out the following site they have clear description on how to use Dialog in LibGDX http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/ui/Dialog.html
My issue has been resolved with the following block code in the Render():
if (Gdx.input.isKeyPressed(Keys.BACK) || Gdx.input.isKeyPressed(Keys.ESCAPE)){
Gdx.input.setCatchBackKey(true);
dialog.setVisible(true);
}

How to perform drag and drop operation on sprite fetched from array and present on screen ?

I want to perform drag and drop operation on these image.
How can i make it possible with the following code.
void storeLocation::changescene()
{
this->removeAllChildren();
//CCDirector::sharedDirector()->replaceScene(storeLocation::scene());
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
//CCScene* scene=CCScene::create();
storeLocation *layer = storeLocation::create();
CCSprite *k=CCSprite::create("background.png");
this->addChild(k,0);
k->setPosition(ccp(visibleSize.width/2+ origin.x, visibleSize.height/2 + origin.y));
CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
this,
menu_selector(storeLocation::menuCloseCallback));
pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width ,
origin.y + pCloseItem->getContentSize().height/2));
pCloseItem->setScale(1.5);
// create menu, it's an autorelease object
CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
pMenu->setPosition(CCPointZero);
this->addChild(pMenu, 1);
this->addChild(pMenu, 1);
this->setTouchEnabled(true);
int l=5;
int posx=0,posy=0;
int count=0,r,j=-1,i=0,flag=0;
int x=20;
int b[30],a[30];
while(count<=5)
{
srand(time(0));
r=rand()%x+1;
flag=checktag(b,r,j);
if(flag==1)
{
b[i]=r;
i++;
count++;
j++;
}
}
int t;
CCObject* jt=NULL;
CCARRAY_FOREACH(images, jt)
{
// CCSize winSize = CCDirector::sharedDirector()->getWinSize();
//float i=winSize.width;
CCSprite *image = dynamic_cast<CCSprite*>(jt);
t=image->getTag();
for(i=0;i<l;i++)
{
if(t==b[i])
{
this->addChild(image);
image->setPosition(ccp(100+posx,100));
posx=posx+120;
}}}
To drag and drop images from one point to another on screen you have to use touch delegate methods
bool ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
void ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
void ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
Detect image on user touch in ccTouchBegan method, for this you can store all image objects in a array and check if touch is in rect of any image by using for loop.
To move the image with user touch change position of touched image(save touched image object in a global object) in ccTouchMoved.
And in ccTouchEnded method do whatever you want to do on droping image.
the easiest way to catch drag and drop events is by implementing the onTouchBegan and onTouchMoved and onTouchEnded methods like this:
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, sprite);
bool HelloWorld::onTouchBegan(Touch* touch, cocos2d::Event* event){
// this method is not needed but in order to implement the onTouchMoved you have to first implement onTouchBegan then the onTouchMoved
return true;
}
void HelloWorld::onTouchMoved(Touch* touch, cocos2d::Event* event){
if (sprite->getBoundingBox().containsPoint(touch->getLocation()))
{
sprite->setPosition(sprite->getPosition() + touch->getDelta());
}
}
void HelloWorld::onTouchEnded(Touch* touch, cocos2d::Event* event){
if (sprite->getBoundingBox().containsPoint(touch->getLocation()))
{
log("Sprite Drop Event");
}
}

Reduce lifecycle validation calls in resizeable Flex LabelItemRenderer

I've subclassed the LabelItemRenderer class to create an expandable renderer for a spark list in my mobile app.
When the user selects an item, the renderer's size increases, additional data is shown. The renderer basically looks like this (I've removed the parts that don't matter here, so this is basically pseudo code).
public class PositionsGridRenderer extends LabelItemRenderer
{
public function PositionsGridRenderer() {
super();
addEventListener(MouseEvent.CLICK, expandHandler);
}
protected override function createChildren():void {
super.createChildren();
_dg = new DataGroup();
_dg.visible = false;
addChild(_dg);
}
private function expandHandler(event:Event):void {
if(_gridVisible) {
if(!_detailClicked) {
_dg.visible = false;
_gridVisible = false;
}
_detailClicked = false;
} else {
_dg.visible = true;
_gridVisible = true;
}
}
public override function set data(value:Object):void {
if(!value) return;
super.data = value;
var pos:Position = data as Position;
label = pos.positionName;
_dg.dataProvider = pos.positionSymbols;
}
protected override function measure():void {
!_gridVisible ? measuredHeight = 30 : measuredHeight = 30 + getElementPreferredHeight(_dg);
this.height = measuredHeight;
}
protected override function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void {
setElementSize(labelDisplay, unscaledWidth, 30);
setElementPosition(labelDisplay, 10,10);
if(_gridVisible) {
setElementSize(_dg, unscaledWidth, getElementPreferredHeight(_dg));
setElementPosition(_dg, 0, 30);
} else {
setElementSize(_dg, unscaledWidth, 0);
}
invalidateSize();
}
}
}
Works as expected, I'm just wondering if there's a way to reduce the amount of validation calls this renderer does when I expand it.
If it is clicked to be expanded the layoutContents and measure functions are both called three times in the following order: layoutcontents -> measure, layoutcontens -> measure, layoutcontents -> measure.
I'd understand them being called once because I invalidate the size, but three times seems odd.
Does anyone know why this is happening, or maybe even how to prevent this from happening?
The real question was why is the component going through three full renderer cycles? After some disussion, this is what we came across:
The first time the invalidate cycle is triggered is when a mouse down, or possibly a touch begin event occurs. This puts the component into the hover state; which causes a visual change in the component.
The second time the invalidate cycle is triggered is when the item is selected. This puts the renderer in the down state; causing a different visual indicator to be drawn.
The third invalidate cycle is caused by the component's own code; when layoutContents() calls invalidatesize()

JTree and dropdown options on right clicking nodes

I'm trying to use the JTree and implement different drop downs for all the parent nodes and the children nodes.
Here's what I've done:
pmTree.addMouseListener(new java.awt.event.MouseAdapter() {
#Override
public void mouseClicked(java.awt.event.MouseEvent evt) {
try {
if(evt.getButton() == evt.BUTTON1) {
}
else if (evt.getButton() == evt.BUTTON3) {
TreePopup(evt);
//pmTree.updateUI();
}
}catch (Exception e) {}
}
});
and PopupCode:
public void TreePopup(java.awt.event.MouseEvent evt) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) pmTree.getLastSelectedPathComponent();
popup = new JPopupMenu();
popup.setInvoker(pmTree);
PopupHandler handler = new PopupHandler(pmTree, popup);
if(node.getLevel() == 1)
{
popup.add(getMenuItem("Parent Node", handler));
}
else if(node.getLevel() == 2)
{
popup.add(getMenuItem("Child", handler));
}
}
and PopUpHandler:
public class PopupHandler extends javax.swing.JFrame implements ActionListener {
JPopupMenu popup;
Point loc;
public PopupHandler(JTree tree, JPopupMenu popup) {
//this.tree = NewJFrame.pmTree;
this.popup = popup;
tree.addMouseListener(ma);
}
and also the
public void actionPerformed(java.awt.event.ActionEvent evt)
for the Child or Parent node being clicked.
However, when I run the program, I get the SAME right click popups for both the child and parent node.
Sorry for the huge chunk of code. I've been stuck with it for 2 days and yet not successful.
Thanks!
Don't go as low-level as a MouseListener, instead use the api around componentPopupMenu. Doing so, the general approach is dynamically configure the componentPopup in the getPopupLocation method, some simple example snippet:
JPopupMenu popup = new JPopupMenu();
final Action action = new AbstractAction("empty") {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
};
popup.add(action);
JTree tree = new JTree() {
/**
* #inherited <p>
*/
#Override
public Point getPopupLocation(MouseEvent e) {
if (e != null) {
// here do your custom config, like f.i add/remove menu items based on context
// this example simply changes the action name
TreePath path = getClosestPathForLocation(e.getX(), e.getY());
action.putValue(Action.NAME, String.valueOf(path.getLastPathComponent()));
return e.getPoint();
}
action.putValue(Action.NAME, "no mouse");
return null;
}
};
tree.setComponentPopupMenu(popup);
You check the selected node:
DefaultMutableTreeNode node = (DefaultMutableTreeNode)pmTree.getLastSelectedPathComponent();
to see if you have a "parent" or a "child" node. You should select the node at the mouse position first, otherwise it will not be the right node. Call
TreePath path = pmTree.getPathForLocation(evt.getX(), evt.getY());
if (path != null) {
pmTree.setSelectionPath(path);
} else {
return;
}
at the beginning of treePopup. (methods in Java should start with a lower case letter!)
Awesome. I was successfully able to put the setSelectionPath() call inside the override of getPopupLocaiton(). I had been trying to do it inside the ActionListener of my JMenuItem to no avail.
public Point getPopupLocation( MouseEvent e ) {
Point point = null;
if( e != null ) {
TreePath path = getClosestPathForLocation( e.getX(), e.getY() );
setSelectionPath( path );
point = e.getPoint();
}
return point;
}