I'm having trouble to handle KeyboardEvents on DartFlash.
I don't know what I'm doing wrong here. Could someone help me?
My intention is to just create a very simple walking character and every time I hit a key, it moves in the x and y, only to start understanding DartFlash API.
Here is the full source code:
class Character extends Sprite
{
TextureAtlas _atlas;
Bitmap _currentBitmap;
int _direction;
String _name;
Character(this._name, this._atlas)
{
this._direction=Direction.down;
this._currentBitmap=this.getBitmap("stand", this._direction);
addChild(this._currentBitmap);
}
String get name => this._name;
Bitmap getBitmap(String name, [int direction, int number])
{
if(direction == null)
{
return new Bitmap(this._atlas.getBitmapData(name));
} else if (number == null)
{
return new Bitmap(this._atlas.getBitmapData("${name}-${Direction.getDirectionName(direction)}"));
}
return new Bitmap(this._atlas.getBitmapData("${name}-${Direction.getDirectionName(direction)}-${number}"));
}
}
Character dk;
void keyboardListener(KeyboardEvent ke) {
print("Key code: ${ke.keyCode}");
dk.x+=1;
dk.y+=1;
}
void main()
{
Stage mainStage = new Stage("mainStage", html.document.query("#mainStage"));
RenderLoop renderLoop = new RenderLoop();
renderLoop.addStage(mainStage);
Resource resource=new Resource();
resource.addTextureAtlas("DarkKnight", "resources/DarkKnight.json", TextureAtlasFormat.JSONARRAY);
resource.load().then((res)
{
print(resource.toString());
dk=new Character("DarkKnight", resource.getTextureAtlas("DarkKnight"));
dk.x=10;
dk.y=10;
mainStage.addChild(dk);
dk.addEventListener(KeyboardEvent.KEY_DOWN, keyboardListener, false);
mainStage.focus=dk;
print("${mainStage.focus.name}");
});
}
There is an easy workaround. Just add an "tabindex" attribute to the canvas element and afterwards you will received KeyboardEvents. If the "tabindex" is not set, then the canvas does not receive keyboard events.
<canvas id="stage" width="800" height="600" tabindex="1"></canvas>
The canvas also needs the focus. You can get the focus by clicking on the canvas or problematically set the focus:
query('#stage').focus();
Related
I have the following code which is supposed to show a clickable icon which opens a popup dialog reading out a lengthy note.
this.capacityCommentColumn = this.facilityGrid.addColumn(
p -> {
if (Strings.isNullOrEmpty(p.getCapacityComment())) {
return null;
} else {
return new ThemeResource("img/note.svg");
}
},
new ImageRenderer<>())
.setWidth(80)
.setCaption("Note");
this.facilityGrid.addItemClickListener(new ItemClickListener<MapQueryService.RowResult>() {
#Override
public void itemClick(Grid.ItemClick<MapQueryService.RowResult> event) {
if (event.getColumn() == capacityCommentColumn && !Strings.isNullOrEmpty(event.getItem().getCapacityComment())) {
final NoteWindow noteWindow = new NoteWindow();
noteWindow.txtDescription.setValue("test");
noteWindow.show();
}
}
});
The problem is the code does not respond to clicks on the actual image, only on the outside. You can see this below. Any idea if its possible to make the image clickable?
You need to add a click listener to the Renderer as well. For example:
Grid<Integer> grid = new Grid();
private void addIconColumn() {
ImageRenderer<Integer> renderer = new ImageRenderer<>();
renderer.addClickListener(e -> iconClicked(e.getItem())); // allow clicks on the image
Grid.Column<Integer, ThemeResource> iconColumn = grid.addColumn(i -> new ThemeResource("img/icon.svg"), renderer)
.setCaption("Icon");
grid.addItemClickListener(e -> { // allow clicks on the cell
if (iconColumn.equals(e.getColumn())) {
iconClicked(e.getItem());
}
});
}
private void iconClicked(Integer i) {
... your UI logic here ...
}
You can see a working example here: https://github.com/alejandro-du/community-answers/tree/master/click-image-in-grid
I could not find any column to set background image inside SearchManager class.
When Google Play app's search result is selected, a background image is displayed but I don't seem to find any public api/column to set it.
Here is my code for content provider's query method
Any idea guys?
Device: Nexus player
#Nullable
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
final String searchKey = (selectionArgs == null) ? "" : selectionArgs[0];
if (!TextUtils.isEmpty(searchKey)) {
// Get title list from search query
ArrayList<TitleSimpleInfo> searchedTitleList = searchTitlesWithKeyword(searchKey);
// return null cursor if no data found
if (searchedTitleList == null || searchedTitleList.isEmpty()) {
return null;
}
// prepare cursor
MatrixCursor matrixCursor = new MatrixCursor(new String[]{
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2,
SearchManager.SUGGEST_COLUMN_CONTENT_TYPE,
SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
SearchManager.SUGGEST_COLUMN_INTENT_DATA,
SearchManager.SUGGEST_COLUMN_RESULT_CARD_IMAGE,
});
// add search result to cursor
for (TitleSimpleInfo title : searchedTitleList) {
matrixCursor.addRow(new Object[]{
title.getTitleName(),
title.getTitleCatch(),
SEARCH_CONTENT_TYPE,
Intent.ACTION_SEARCH,
SEARCH_INTENT_DATA + title.getTitleCode(),
SEARCH_IMAGE_HEADER + title.getThumbnailUrl(),
});
}
return matrixCursor;
} else {
return null;
}
}
I want to do the ill-advised and place both an onClick and onDoubleClick on the same element with each type of event resulting in a different action. Specifically on an image, click to advance to the next image, double-click to toggle fullscreen.
Naturally I get two clicks followed by a double-click (though I understand that some browsers only fire one click before the double-click).
I had thought to make it easy on myself and place each event into a buffer (List) - or rather to add the event.type string to a list, then, after a suitable elapse of time, say 250 or 300 milliseconds examine the last item in the buffer and if doubleclick then go fullscreen else advance the length of the list.
I have found that the list only ever has one item, and I have not worked out how to get the timer to work..
Amongst my attempts was this one:
void catchClickEvents(Event e) {
var eventTypes = new List<String>();
eventTypes.add(e.type);
Duration duration = const Duration(milliseconds: 300);
var timeout = new Timer(duration, () => processEvents(eventTypes));
}
void processEvents(List eTypes) {
// just to see what is going on...
print(eTypes);
}
this results in this output printed to the console:
[click]
[click]
[dblclick]
rather than
[click, click, dblclick]
If I slow it down there is a clear delay before those three event types are printed together
So...
The bigger question is
'What is the darty way to distiguish between single and double-click and perform a different action for each?'
The other questions are:
How do I fill a buffer with successive events (and later clear it down) - or even how do I use Dart's Stream of events as a buffer...
What is the real way timeout before examining the contents of the buffer?
(and I guess the final question is 'should I abandon the effort and settle for a conventional set of buttons with glyph-icons?'!)
I'm not sure if IE still has the event sequence explained here (no 2nd click event)
https://stackoverflow.com/a/5511527/217408
If yes you can use a slightly deviated variant of Roberts solution:
library app_element;
import 'dart:html' as dom;
import 'dart:async' as async;
Duration dblClickDelay = new Duration(milliseconds: 500);
async.Timer clickTimer;
void clickHandler(dom.MouseEvent e, [bool forReal = false]) {
if(clickTimer == null) {
clickTimer = new async.Timer(dblClickDelay, () {
clickHandler(e, true);
clickTimer = null;
});
} else if(forReal){
print('click');
}
}
void dblClickHandler(dom.MouseEvent e) {
if(clickTimer != null) {
clickTimer.cancel();
clickTimer = null;
}
print('doubleClick');
}
void main() {
dom.querySelector('button')
..onClick.listen(clickHandler)
..onDoubleClick.listen(dblClickHandler);
}
or just use Roberts solution with the mouseUp event instead of the click event.
The problem is that your variable is not global.
var eventTypes = new List<String>();
void catchClickEvents(Event e) {
eventTypes.add(e.type);
Duration duration = const Duration(milliseconds: 300);
var timeout = new Timer(duration, () => processEvents(eventTypes));
}
void processEvents(List eTypes) {
print(eTypes);
}
There also is e.detail that should return the number of the click. You can use that, if you don't need the Internet Explorer. The problem with your list is that it grows and never gets cleared.
Let's take into account what we know: You get click events and at somepoint you have doubleclicks.
You could use a click counter here. (Or use e.detail) to skip the second click event. So you only have click and dblclick.
Now when you get a click event, you create a new timer like you did before and run the click action. If you get the dblclick event you simply run you action. This could like this:
DivElement div = querySelector('#div');
Timer timeout = null;
div.onClick.listen((MouseEvent e) {
if(e.detail >= 2) {
e.preventDefault();
} else {
if(timeout != null) {
timeout.cancel();
}
timeout = new Timer(new Duration(milliseconds: 150), () => print('click'));
}
});
div.onDoubleClick.listen((MouseEvent e) {
if(timeout != null) {
timeout.cancel();
timeout = null;
}
print('dblclick');
});
This is the example code that works for me. If you can't rely on e.detail just us a counter and reset it after some ms after a click event.
I hope this helps you.
Regards, Robert
Your page should react on user inputs as fast as possible. If you wait to confirm double click - your onClick will become much less responsive. You can hide the problem by changing visual state of the element(for example, playing animation) after first click but it can confuse user. And it gets even worse with handheld. Also if element has to react only on onClick event, you can "cheat" and listen to onmousedown instead - it will make your UI much more responsive.
On top of all this, double click, from client to client, may have noticeably different trigger time interval and it isn't intuitive, for user, that you can double click something. You will have to bloat your interface with unnecessary hints.
edit: Added my solution. It should be fairly extensible and future proof.
import 'dart:html';
import 'dart:async';
import 'dart:math';
//enum. Kinda... https://code.google.com/p/dart/issues/detail?id=88
class UIAction {
static const NEXT = const UIAction._(0);
static const FULLSCREEN = const UIAction._(1);
static const ERROR = const UIAction._(2);
final int value;
const UIAction._(this.value);
}
/**
*[UIEventToUIAction] transforms UIEvents into corresponding UIActions.
*/
class UIEventToUIAction implements StreamTransformer<UIEvent, UIAction> {
/**
* I use "guesstimate" value for [doubleClickInterval] but you can calculate
* comfortable value for the user from his/her previous activity.
*/
final Duration doubleClickInterval = const Duration(milliseconds: 400);
final StreamController<UIAction> st = new StreamController();
Stream<UIAction> bind(Stream<UIEvent> originalStream) {
int t1 = 0,
t2 = 0;
bool isOdd = true;
Duration deltaTime;
originalStream.timeout(doubleClickInterval, onTimeout: (_) {
if ((deltaTime != null) && (deltaTime >= doubleClickInterval)) {
st.add(UIAction.NEXT);
}
}).forEach((UIEvent uiEvent) {
isOdd ? t1 = uiEvent.timeStamp : t2 = uiEvent.timeStamp;
deltaTime = new Duration(milliseconds: (t1 - t2).abs());
if (deltaTime < doubleClickInterval) st.add(UIAction.FULLSCREEN);
isOdd = !isOdd;
});
return st.stream;
}
}
void main() {
//Eagerly perform time consuming tasks to decrease the input latency.
Future NextImageLoaded;
Future LargeImageLoaded;
element.onMouseDown.forEach((_) {
NextImageLoaded = asyncActionStub(
"load next image. Returns completed future if already loaded");
LargeImageLoaded = asyncActionStub(
"load large version of active image to show in fullscreen mode."
"Returns completed future if already loaded");
});
Stream<UIEvent> userInputs = element.onClick as Stream<UIEvent>;
userInputs.transform(new UIEventToUIAction()).forEach((action) {
switch (action) {
case UIAction.FULLSCREEN:
LargeImageLoaded.then( (_) =>asyncActionStub("fullscreen mode") )
.then((_) => print("'full screen' finished"));
break;
case UIAction.NEXT:
NextImageLoaded.then( (_) =>asyncActionStub("next image") )
.then((_) => print("'next image' finished"));
break;
default:
asyncActionStub("???");
}
});
}
final DivElement element = querySelector("#element");
final Random rng = new Random();
final Set performed = new Set();
/**
*[asyncActionStub] Pretends to asynchronously do something usefull.
* Also pretends to use cache.
*/
Future asyncActionStub(String msg) {
if (performed.contains(msg)) {
return new Future.delayed(const Duration(milliseconds: 0));
}
print(msg);
return new Future.delayed(
new Duration(milliseconds: rng.nextInt(300)),
() => performed.add(msg));
}
i'm creating one application in which i get gift images with id's from web server through JSON. When i click on any gift image, it goes on next page where it shows all information of that image (get image information with its id from web server through JSON).
Problem is: When i click on any gift image on page to see its relevant information, it gets the last gift image id every time, i want when i click on any image, it gets the specific image id which i click. How it is possible??
Screenshot of the page is : http://ugo.offroadstudios.com/gifts.png
Here is sample code:
public class Gifts extends MainScreen {
String giftsid;
BitmapField giftimg;
public Gifts(){
setTitle("Gift Store");
creategifts();
}
public void creategifts()
{
//Link URL
String strURL = "http://ugo.offroadstudios.com/api/frndgift/?loginusername=adil;deviceside=true";
webConnection wb = new webConnection();
String res = wb.getJson(strURL);
try {
JSONObject object = new JSONObject(res);
if(object.getString("status") == "error")
{
Dialog.alert("Invalid "+object.getString("status"));
}
else
{
int totalgifts;
totalgifts = object.getInt("totalgifts");
Bitmap listThumb;
JSONArray imagearr;
JSONArray giftsidarr;
String imgname;
Bitmap bmpResized;
for(int i=0; i < totalgifts; i++){
imagearr = object.getJSONArray("gifts_image");
imgname = imagearr.getString(i);
giftsidarr = object.getJSONArray("gifts_id");
giftsid = giftsidarr.getString(i);
listThumb = getImage.getImageFromUrl("http://ugo.offroadstudios.com/wp-content/plugins/bp-gifts-rebirth/includes/images/"+imgname+";deviceside=true");
bmpResized = GPATools.ResizeTransparentBitmap(listThumb, 80, 80,
Bitmap.FILTER_LANCZOS, Bitmap.SCALE_TO_FIT);
giftimg =new BitmapField(bmpResized,FOCUSABLE)
{
protected boolean navigationClick(int status, int time)
{
Dialog.alert("giftsid "+giftsid);
UiApplication.getUiApplication().pushScreen(new SendGift(giftsid));
return true;
}
};
add(giftimg);
}
}
}
catch (JSONException e) {
System.out.println("EX is "+e);
e.printStackTrace();
}
}
}
You are always getting the gift id of the last gift in the list because you have created your buttons with this code:
giftimg =new BitmapField(bmpResized,FOCUSABLE)
{
protected boolean navigationClick(int status, int time)
{
Dialog.alert("giftsid "+giftsid);
UiApplication.getUiApplication().pushScreen(new SendGift(giftsid));
return true;
}
};
Your navigationClick() method used the giftsid variable, which is a persistent member variable of your class. You assign this variable in your for loop, so the final value it keeps is the last value assigned in the loop (giftsidarr.getString(totalgifts)).
Although you declare the navigationClick() method in a loop where the giftsid is many different values, the navigationClick() method uses the value of giftsid when it is run. The last value.
There's many ways to fix it. You can use a separate constant value in your loop:
final String nextGiftsId = giftsid;
giftimg =new BitmapField(bmpResized,FOCUSABLE)
{
protected boolean navigationClick(int status, int time)
{
Dialog.alert("nextGiftsId= "+nextGiftsId);
UiApplication.getUiApplication().pushScreen(new SendGift(nextGiftsId));
return true;
}
};
Or, as Signare suggested, attach a cookie to each button that identifies its corresponding gift:
giftimg =new BitmapField(bmpResized,FOCUSABLE)
{
protected boolean navigationClick(int status, int time)
{
String giftId = (String)getCookie(); // read gift id from the cookie
Dialog.alert("giftId= "+giftId);
UiApplication.getUiApplication().pushScreen(new SendGift(giftId));
return true;
}
};
giftimg.setCookie(giftsid); // set the cookie after creating the field
Inside your for loop, add the following code -
giftimg[i].setChangeListener(this);
Then -
public void fieldChanged(Field field, int context) {
for(int i=0;i<totalgifts;i++) {
if(field == giftimg[i]) {
// you can trigger your event
}
}
EDIT :-
giftimg[i].setChangeListener(listener);
listener = new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
if ( field instanceof BitmapField ) {
for(int i=0;i<totalgifts;i++) {
if ( field == giftimg[i] ) {
// you can trigger your event
}
}
}
}
};
I'm writing a Fragment that uses a loader to get a Cursor containing data about locations of various things on a map. I've inherited code to sort these locations by distance from the device, or from a search location; distance metrics aren't something that's particularly easy to implement in SQL, so rather than use a CursorAdapter (as elsewhere) I'm loading the data once from the Cursor and then sorting it afterwards.
I have just one problem: when the web service returns a new set of locations (for example, on first load), the list isn't updating. I've registered a ContentObserver on the Cursor and it is being hit when I call notifyChange(...) in the ContentProvider; it's just that the Cursor I've stored from the original load still has a count of zero.
The callbacks and the ContentObserver look like this:
private LoaderCallbacks<Cursor> mCallbacks = new LoaderCallbacks<Cursor>() {
public void onLoaderReset(Loader<Cursor> loader) {
mLoaderCreated = false;
mCursor.unregisterContentObserver(mObserver);
mCursor = null;
}
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if(cursor!=mCursor) {
if(mCursor!=null)
mCursor.unregisterContentObserver(mObserver);
cursor.registerContentObserver(mObserver);
mCursor = cursor;
if(cursor.isClosed()) {
getLoaderManager().restartLoader(mFragmentId, null, mCallbacks);
return;
}
}
mDataModel.populate(cursor);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
mLoaderCreated = true;
triggerServicesFeed();
CursorLoader cursorLoader = null;
if(id == mFragmentId) {
cursorLoader = new CursorLoader(getActivity(),
FerrariVertuContentProvider.SERVICES_URI,
null, null, null,null);
}
return cursorLoader;
}
};
private ContentObserver mObserver = new ContentObserver(null) {
public void onChange(boolean selfChange, android.net.Uri uri) {
onChange(selfChange);
};
public void onChange(boolean selfChange) {
if(mCursor.isClosed()) {
mCursor.unregisterContentObserver(this);
mCursor = null;
} else {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
//mCursor still reports zero on first run
mDataModel.populate(mCursor);
}
});
}
};
};
I know CursorAdapter just updates when the Cursor updates, and the fact that I'm getting update events when I'd expect to makes me think this stage of the process, at least, is working. How do I either get mCursor to give me the new data, or get a fresh Cursor representing the new data?