JavaFXPorts Kokos Project doesn't show the Android keyboard - android

I am modifying the kokos project with JavaFX and Android, with the jfxmobile plugin and when I add a textfield the on-screen keyboard does not appear and I can not modify the text.
mytextfield is an object of the TextField class of JavaFX:
#FXML
public void initialize(){
counter = 0;
mytextfield.setStyle( "-fx-background-color:#FFFF00; -fx-skin: \"com.sun.javafx.scene.control.skin.TextFieldSkinAndroid\"; ");
mytextfield.requestFocus();
}
public void onButtonClick(){
counter++;
clickLabel.setText("You've clicked this button " + counter + " times!");
}
What could be happening?

I've tested the Kokos project, modifying the JavaFX application class to include a JavaFX TextField:
#Override
public void start (Stage stage) throws Exception {
final Button b = new Button("Click JavaFX");
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
b.setText("Clicked");
}
});
Screen primaryScreen = Screen.getPrimary();
Rectangle2D visualBounds = primaryScreen.getVisualBounds();
double width = visualBounds.getWidth();
double height = visualBounds.getHeight();
VBox box = new VBox(10, b, new TextField());
box.setAlignment(Pos.CENTER);
Scene s = new Scene(box, width, height);
stage.setScene(s);
stage.show();
}
and I can reproduce the issue: the soft keyboard doesn't show up.
For starters you don't need to set the -fx-skin property for the TextField, it will be applied internally.
If you check the logs, with adb logcat or from AndroidStudio, you will notice that there is a call to show the keyboard when the textField gets the focus, and another one to hide it when it loses it:
V/FXEntity: Called notify_showIME
V/FXEntity: Done calling notify_showIME
...
V/FXEntity: Called notify_hideIME
V/FXEntity: Done Calling notify_hideIME
This means that the JavaFX TextField actually do the right calls to show and hide the keyboard, but something is failing.
After some debugging, I noticed that the activity_main.xml was using this for the fragment definition:
android:name="android.webkit.WebViewFragment"
which corresponds with a built-in fragment intended to show a WebView.
That is not what we need here, so I created a fragment:
public class MyFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fx_fragment, container, false);
return view;
}
}
based on the existing fx_fragment.xml, and modified activity_main.xml accordingly:
<fragment
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:name="javafxports.org.kokos.MyFragment"
android:id="#+id/fragment"
android:layout_gravity="left|top"
tools:layout="#layout/fx_fragment" />
And this is it, now when you run the app and the textField gets the focus, the keyboard shows up, and you can type in it.

Related

Android: How to call a function on click of an invisible button?

I have an image on my screen and want the user to touch a specific region of the image. I have placed an invisible button on top of the region I want the user to click and set an onClickListener on the same.
From what I read, INVISIBLE buttons still acquire the space they are at, unlike GONE buttons.
However, the onClick method is not invoked if I click the region. Is there a fact I am missing or an alternate way to achieve this?
public class InteractiveFragment extends Fragment implements View.OnClickListener, OnClickableAreaClickedListener {
private Button btnEye;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_interactive, container, false);
btnEye = view.findViewById(R.id.btnEye);
btnEye.setOnClickListener(this);
btnEye.setVisibility(View.INVISIBLE);
}
#Override
public void onClick(View v) {
Toast.makeText(getContext(), "Correct", Toast.LENGTH_SHORT).show();
System.out.println("method invoked");
}
]
you should know the android event dispatch mechanism. we can see
it in the ViewGrouop dispatchOnTouchEvent method source code.
here is the core code:
//child is the view
if (!child.canReceivePointerEvents()|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
// the canReceivePointerEvents method in view
protected boolean canReceivePointerEvents() {
return (mViewFlags & VISIBILITY_MASK) == VISIBLE || getAnimation() != null;
}
we can get when the view is INVISBLE, it can not receive the event, the parent don't dispatch event to it.
so if you want to respond to click event, you have to set it VISIBLE, set the button background transparent.
btn.setVisibility(View.VISIBLE);
btn.setbackgroundcolor(Color.Transparent);
Set the background to transparent and added a finite size to the button. Courtesy #Sam
Used the following xml config for the button
<Button
android:id="#+id/btnEye"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#android:color/transparent"
tools:visibility="visible" />

Codenameone Form adds another title instead of updating when Toolbar added

I'm running into a strange issue when adding a Toolbar to my Form in my Codenameone app. If I set a toolbar on my form, it shows another title with the toolbar hamburger and new title below the title of the previous form instead of replacing it like I would expect. It looks like this:
The functionality works fine replacing the old title like I would expect when I run in the Codenameone simulator, but I get this weird behavior shown in the image when I make an Android build and run it on a Nexus 5 (6.0.1). The back arrow and "12 of 12" is the title from the previousForm
This is my code, am I doing anything wrong here with the Toolbar usage?
void goShowResource(final Form previousForm) {
previous = previousForm;
final Toolbar bar = new Toolbar();
final Form rd = new Form("resource details");
final Resource thisResource = this;
rd.setToolbar(bar);
bar.addCommandToSideMenu(new Command("command 1") {
#Override
public void actionPerformed(ActionEvent evt) {
AddResources ar = new AddResources(settings, thisResource);
ar.goAddResources(rd);
}
});
bar.addCommandToSideMenu(new Command("command 2") {
#Override
public void actionPerformed(ActionEvent evt){
UpdateResource ur = new UpdateResource(settings);
ur.goUpdateResource(rd, thisResource);
}
});
rd.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
showDetails(rd);
rd.show();
}
edit: Additonal info, if I open the sidemenu once, the old title bar at the top shrinks away, and I'm left with the the single correct yet incorrectly formatted title area.
You should use the Toolbar for all the forms in the app or disable the default which is native menu bar when working with the toolbar. You can do the latter by editing the theme and selecting the constants tab then pressing "Add" and selecting commandBehavior=Side.
Android currently defaults to the native ActionBar behavior and Toolbar implicitly overrides that, however when a transition occurs from the native to the lightweight component things can get pretty hairy (and might also look unnatural) so we recommend picking one UI paradigm and going with it.
Since the ActionBar is a volatile API we recommend Toolbar going forward as its far more customizable and gives us a lot of control.
This can be fixed by removing all command from the form after setting the toolbar, then add a fresh back command to the toolbar if required.
void goShowResource(final Form previousForm) {
previous = previousForm;
final Toolbar bar = new Toolbar();
final Form rd = new Form("resource details");
final Resource thisResource = this;
rd.removeAllCommands();
rd.setBackCommand(null);
rd.setToolbar(bar);
//Add back command
Command back = new Command("back") {
#Override
public void actionPerformed(ActionEvent evt) {
previousForm.showBack();
}
};
bar.addCommandToSideMenu(back);
bar.addCommandToSideMenu(new Command("command 1") {
#Override
public void actionPerformed(ActionEvent evt) {
AddResources ar = new AddResources(settings, thisResource);
ar.goAddResources(rd);
}
});
bar.addCommandToSideMenu(new Command("command 2") {
#Override
public void actionPerformed(ActionEvent evt){
UpdateResource ur = new UpdateResource(settings);
ur.goUpdateResource(rd, thisResource);
}
});
rd.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
showDetails(rd);
rd.show();
}

addView inconsistent behavior

I have a custom component that extends RelativeLayout which in turns holds a GridLayout(named mFormLayout). I have a public method that adds two spinners with their proper adapter source and an imageview which acts as a button to remove rows.
public class EditableTwinSpinnerGridForm extends EditableGridForm
{
public void addTwinSpinnerRow(final Locale.MapKey spinner1DefVal, final Locale.MapKey spinner2DefVal)
{
Spinner spinner1 = createSpinner(mTSP.getFirstSpinnerRes(), spinner1DefVal.getId());
spinner1.setOnItemSelectedListener(mTSP.getIsl());
Spinner spinner2 = createSpinner(mTSP.getSecondSpinnerRes(), spinner2DefVal.getId());
ImageView rmvBtn = createRemoveBtn();
mFormLayout.addView(spinner1);
mFormLayout.addView(spinner2);
mFormLayout.addView(rmvBtn);
}
}
For some reason, this method works when I am adding rows from a call to onCreate in an activity, but when I am calling this method after the activity is created(from an onclicklistener) the Spinners are either not there or only one of them shows up. They do take the space because I see the row and the removable image view.
I have also noticed that when I focus on a EditText in the same activity and the keyboard pops up, the added spinners show up when I press back to remove the keyboard.
Here's the code I use to create a spinner :
protected Spinner createSpinner(Integer spinnerSrc, String defaultSpinnerValue)
{
Spinner spinner = new Spinner(mCtx, Spinner.MODE_DIALOG);
// Setting the bg color to the containing color to remove the spinner arrow.
spinner.setBackgroundResource(R.color.container_bg);
SparseArray<Phrase> map = Locale.getInstance().getMap(spinnerSrc);
PhraseArrayAdapter adapter = createSpinnerFromMap(spinnerSrc);
spinner.setAdapter(adapter);
if (defaultSpinnerValue.equals(Utilities.EMPTY_STRING) || defaultSpinnerValue.isEmpty())
{
throw new IllegalStateException();
}
else
{
Utilities.getInstance().setMapSpinnerPosByValue(map, defaultSpinnerValue, spinner);
}
adapter.setDropDownViewResource(R.layout.editable_spinner_dropdown_item);
setSpinnerLayoutParams(spinner);
return spinner;
}
protected void setSpinnerLayoutParams(Spinner spinner)
{
GridLayout.LayoutParams lp = createDefaultGridParams();
lp.setGravity(Gravity.FILL_HORIZONTAL);
lp.width = 250;
lp.rightMargin = 0;
spinner.setLayoutParams(lp);
}
The code works when the activity is loaded so I'm a bit stumped. I looked around and some people suggested I set LayoutParams in addView, but why would this method work in onCreate, but not afterwards?
Here's what's happening visually(The first three rows are added from a loop in onCreate(), the two second ones are added by pressing "Add +"). As you can see the second spinner isn't showing up, sometimes both aren't showing up. I also tried calling invalidate and requestLayout to no avail.
I had looked into the invalidate method, here's where it is located currently :
public abstract class EditableForm extends RelativeLayout implements ObservableInt
{
private class OnAddClicked implements OnClickListener
{
#Override
public void onClick(View v)
{
onAddClicked(v);
EditableForm.this.invalidate();
mFormLayout.invalidate();
}
}
}
Which calls(In a subclass of EditableGrid) :
#Override
protected void onAddClicked(View clickedView)
{
addTwinSpinnerRow();
notifyObservers(new ObservableData(EDITABLE_ADD_GRID_CLICKED, null));
}
protected void addTwinSpinnerRow()
{
Locale.MapKey v1 = Locale.getInstance().getMap(mTSP.getFirstSpinnerRes()).get(0).getMapId();
Locale.MapKey v2 = Locale.getInstance().getMap(mTSP.getSecondSpinnerRes()).get(0).getMapId();
addTwinSpinnerRow(v1, v2);
}
Have you tried calling the Invalidate method of the container view rather than the added view?
Most likely the views you are adding are there, they just need to be drawn which is suggested by your keyboard hide/show difference. Does rotating the device also cause them to appear? If so, this again suggests that you need to redraw your custom layout.
When it's necessary to execute invalidate() on a View?

Android: KeyboardView doesn't appear above EditText

I'm trying to have an EditText and the possibility to display a keyboard at the bottom of the screen when the user taps the EditText. I'm aware of the InputMethodService and the SoftKeyboard example, but I can't use it in that fashion as my keyboard should only be available to this very EditText.
Furthermore, there should be a context menu, but that's not part of this question (I think).
I've read plenty of code fragments, but in many cases they contain methods that aren't available anymore (i.e., getViewInflate()) or are written in a context that I don't understand or can't translate into my code (mind that I'm a newbie regarding Android).
In most attempts I fail with this exception when I tap the EditText:
java.lang.IllegalArgumentException: width and height must be > 0
followed by a stack-trace that doesn't contain any of my classes. As you can see in the code below all sizes are set.
What you see below is the current status of the code (I removed some of the code and I hope it still makes sense). I also tried to use what's inside of handler.post() in the main thread, use the commented stuff instead of the handler.post() ...
What's not below is an attempt to use a RelativeLayout with the EditText and the KeyboardView in one layout-XML. There was a different exception, something like "invalid type 0x12" or something when creating the layout.
It just doesn't work or I just don't know how to do it. Can anyone please guide me through this? Please let me know if something is missing.
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<EditText
android:id="#+id/field_input"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:inputType="textMultiLine|textImeMultiLine"
android:typeface="monospace"
android:gravity="top|left"
android:maxLength="255"
/>
</LinearLayout>
keyboard.xml:
<?xml version="1.0" encoding="utf-8"?>
<com.messenger.keyboard.LatinKeyboardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/keyboard"
android:layout_alignParentBottom="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
LatinKeyboardView.java:
import android.inputmethodservice.KeyboardView;
public class LatinKeyboardView extends KeyboardView {
:
}
EditorActivity.java
import android.app.Activity;
public class EditorActivity extends Activity {
private View keyboardLayout;
#Override
public void onCreate(Bundle savedInstanceState) {
final EditText inputField;
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
keyboardLayout = (View)getLayoutInflater().inflate(R.layout.keyboard, null, false);
inputField = (EditText)findViewById(R.id.field_input);
registerForContextMenu(inputField);
inputField.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//PopupWindow pw = new PopupWindow(inflater.inflate(R.layout.input, null, false), 100, 100, true);
PopupWindow pw = new PopupWindow(keyboardLayout, 100, 100, true);
pw.showAtLocation(findViewById(R.id.field_input), Gravity.CENTER, 0, 0);
}
});
/*
if (keyboardLayout.getVisibility() == View.GONE) {
// Show Media Player
TranslateAnimation mAnimUp =
new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, -keyboardLayout.getHeight(),
Animation.RELATIVE_TO_SELF, 0);
mAnimUp.setStartOffset(500);
mAnimUp.setDuration(500);
keyboardLayout.setVisibility(View.VISIBLE);
keyboardLayout.setAnimation(mAnimUp);
}
*/
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
:
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
:
}
#Override
public boolean onOptionsItemSelected(final MenuItem item) {
:
}
#Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
:
}
}
After hours of "research and trying" stuff, I finally understood my mistake, which seems to be "sjngm"'s one. In order for a virtual keyboard to render, you must
declare the view either by inflating a layout xml file, or by declaring in row your KeyboardView (just as you would do it for an other View).
And what was forgotten here: retrieve your KeyboardView with findViewById() and call on it:
keyboardViewInstance.setKeyboard(new Keyboard(...) );
That's it. You will be able to see your keyboardView on the screen! Of course, you need to either create your own Keyboard class, or to use the existing one with an xml resource file defining your keyboard keys (res/xml/keyboard.xml).
I'm currently reinventing my approach as I think that I didn't break the InputMethodService enough to have it work without itself. In other words I threw away the sample and started from scratch to get the layout working (it's now one layout instead of two) and then added the code from the sample to handle the inputs properly.
After further research I found a really helpful question about an App-specific soft-keyboard. If you run into my situation, look there.

Android: why is my OnKeyListener() not called?

I defined an EditText-field and I want to be informed when the user edits that fields.
So I thought: simple - I add an OnKeyListener and so I did. But even though the text field gets edited (and even displays the entered/modified text) I don't get any callback, i.e. the LOG-output doesn't show up.
TextView text = new TextView(this);
text.setText(...);
...
text.setOnKeyListener(new OnKeyListener()
{
public boolean onKey(View v, int keyCode, KeyEvent event) {
TextView tv = (TextView)v;
CharSequence val = tv.getText();
Log.v(TAG, "value: " + val);
// ... rest omitted for brevity
}
});
Any idea, why that callback is never called?
Michael
PS.: Sigh! Android is really full of oddities! It seems that almost nothing I touched so far worked immediatly as one would expect. And - believe it or not - I have LOTS of experience with GUIs, esp. in Java (AWT, Swing, SWT, you name it...) But Android is a really tough beast!
Are you using the soft keyboard (ime) to type in the edit text? I believe that the onKeyListener only gets invoked with events from the hardware keyboard. You are better off using the TextWatcher if you can. onKeyListener not working with soft keyboard (Android)
I had the exact same problem, but on only 1 of my Android apps and I never did figure out what the difference was.
My solution though was to do what Mayra suggested and add a TextWatcher to handle the TextChanged events. So it works no matter how the text entry occurs.
editName.addTextChangedListener(new TextWatcher () {
public void afterTextChanged(Editable s) {
Button btnSave = (Button)findViewById(R.id.btnSaveToon);
if(s.length() > 0)
btnSave.setEnabled(true);
else
btnSave.setEnabled(false);
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
}
});
Works like a charm in the emulator and on my HTC Inspire
You say that you're dealing with an EditText, but your code refers to a TextView. My guess is that you have an EditText in your layout XML files, but you're referring to this newly created TextView in your code, which is in fact not even in the app's UI at all.
If there is already an EditText in your layout XML file, then you need to get a pointer to it in your Java code, probably using the findViewById() method. Then add your OnKeyListener to that EditText.
Defining your layout in XML actually makes a lot more sense (at least in many, if not most, cases) than defining it one component at a time and then adding each those components to the UI, like you do in Swing. But it takes some getting used to, no question.
I had the same problem. The goal of the EditText was to input an amount of a currency, so I only needed the KeyEvents because I wanted the amount to be written from back to front as in apps like PayPal. I ended up just generating my own soft keyboard at the bottom of a RelativeLayout using a Fragment that is toggled with its visibility. If anyone wants to use my code, here you go:
The Keyboard Fragment Class:
public class KeyboardFragment extends Fragment {
private LinearLayout keyboard1, keyboard2, keyboard3, keyboard4, keyboard5, keyboard6,
keyboard7, keyboard8, keyboard9, keyboard0, keyboardReturn, keyboardApply;
private KeyboardListener keyboardListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_keyboard, container, false);
keyboard0 = view.findViewById(R.id.keyboard0);
keyboard1 = view.findViewById(R.id.keyboard1);
//and so on...
keyboard0.setOnClickListener((View v) -> {
keyboardListener.keyPressed(0);
});
keyboard1.setOnClickListener((View v) -> {
keyboardListener.keyPressed(1);
});
//and so on...
return view;
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
keyboardListener = (KeyboardListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "must implement KeyboardListener");
}
}
public interface KeyboardListener {
public void keyPressed(int key);
}
}
The overlaying activity needs to implement the KeyboardListener and override the keyPressed function.
The XML of the Fragment looks like this:
Drawables:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#color/ui_keyboard_dark" />
<corners android:radius="5dp" />
</shape>
Then I inflated a container in the overlaying activity with the fragment and set its height to a moderate fraction of the screen's height:
KeyboardFragment fragment = new KeyboardFragment();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.keyboardContainer, fragment);
fragmentTransaction.commit();
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,(int) ((double)displayMetrics.heightPixels / 2.8));
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
keyboardContainer.setLayoutParams(layoutParams);
Set a boolean that tracks the state of the keyboard in the activity. Then set the visibility according to actions like onBackPressed() or the click of the apply or ok button of the keyboard.
The amount of the currency is tracked using an int that would represent 10,95€ like this: amount = 1095
Then you just need to multiply the amount by 10 and add the pressed number. When pressing backspace just divide by 10.
I hope someone faces the same problem as me and finds this useful :).

Categories

Resources