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.
Related
I have a need to show a minimally-intrusive non-blocking notification which is not tied to the activity it was shown in (like a Toast) and which is clickable. Anyone have any idea whether or not this is possible? Unfortunately, it appears that Toast notifications (custom or otherwise) are not clickable (i.e. setting an OnClickListener on its views has no effect). All the alternatives that I'm aware of (i.e. AlertDialog, PopupWindow and Crouton) seem to show a notification which is tied to the activity it was shown in (i.e. they won't continue showing when the activity finishes). Any suggestions?
You can use PopupWindow, add an onClickListener and add a handler to auto cancel it after n times (just like the behavior of a toast). Something like this:
public static void showToast(Activity a, String title, String message) {
// inflate your xml layout
LayoutInflater inflater = a.getLayoutInflater();
View layout = inflater.inflate(R.layout.custom_toast,
(ViewGroup) a.findViewById(R.id.toast_layout_root));
// set the custom display
((TextView) layout.findViewById(R.id.title)).setText(title);
((TextView) layout.findViewById(R.id.message)).setText(message);
// initialize your popupWindow and use your custom layout as the view
final PopupWindow pw = new PopupWindow(layout,
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT, true);
// set windowType to TYPE_TOAST (requires API 23 above)
// this will make popupWindow still appear even the activity was closed
pw.setWindowLayoutType(WindowManager.LayoutParams.TYPE_TOAST);
pw.showAtLocation(layout, Gravity.CENTER | Gravity.TOP, 0, 500);
// handle popupWindow click event
layout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// do anything when popupWindow was clicked
pw.dismiss(); // dismiss the window
}
});
// dismiss the popup window after 3sec
new Handler().postDelayed(new Runnable() {
public void run() {
pw.dismiss();
}
}, 3000);
}
xml layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/toast_layout_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
android:orientation="vertical"
android:elevation="10dp"
android:padding="20dp">
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#FFF"
android:textStyle="bold"/>
<TextView
android:id="#+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#FFF"/>
</LinearLayout>
You are right, a Toast object has no way to be interacted with, but there are many libraries out there that will give you the same look and feel as a toast, but with some interactivity. The one I use is https://github.com/JohnPersano/SuperToasts
I think what you need is in fact a PopupWindowwhich can be seen here "http://developer.android.com/reference/android/widget/PopupWindow.html".
Toasts have a very specific task, which is to inform the user, without any input from them. So instead of trying to extend the purpose of the Toast, use the PopupWindow which can be interacted with by the user.
A 'Dialog' type of activity will probably be your best bet.
In manifest:
<activity android:name=".ToastLikeActivity"
android:theme="#android:style/Theme.Dialog"
android:label="#string/label"
></activity>
And timeout the activity within the onCreate():
class ToastLikeActivity extends Activity {
#Override
public void onCreate(Bundle state)
// auto-kill activity after X seconds <-------------------------
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
ToastLikeActivity.this.finish(); // kill after X seconds
}
}
}, VisibleTimeSecs*1000);
}
To display the dialog start it as with any other activity:
Intent i = new Intent(this, ToastLikeActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
And it will show up and automatically go away after X seconds.
Such a popup will not be tied to the caller activity. In fact - it will not even require a caller activity. You
can activate it (bad idea, but possible) even from a service.
You can implement basically any kind of sensitive (i.e. accepting user's clicks) interface you want to
the ToastLikeActivity. Especially: you can make its exteriors transparent, giving it a dialog-likke looks.
Is it possible to insert a button at the bottom of a working 3d animation done with Min3d framework in android. I do have a working 3d model car which is rotating. I want to put a button at the bottom of this working animation screen. Is it possible since i am not using any layout.Please explain me with an example so that i can replicate the same with my program.
Yes. Rather, the procedure is to rather add the min3d framework to the layout, using glSurfaceView. The following cocde highlights how to achieve the same, assuming you have the other basics (such as loading the object ) covered. If not, links for the same is provided at the end of this brief.
//import statements
public class threedviewActivity extends RendererActivity implements View.OnClickListener,View.OnTouchListener{
//onClick and onTouch in case you want to recognize both touch and click on the button
//initialize the variables you require
#Override
public void onCreateSetContentView() {
setContentView(R.layout.activity_example);
RelativeLayout R1 = (RelativeLayout)this.findViewById(R.id.SceneHolder);
R1.addView(_glSurfaceView); //adding the min3d view into the layout
Button example_1 = (Button)this.findViewById(R.id.example_1);
Button example_2 = (Button)this.findViewById(R.id.example_2);
example_1.setOnTouchListener(this);
example_2.setOnClickListener(this);
}
public boolean onTouch(View $v, MotionEvent event) {
switch($v.getId())
{
case R.id.example_1:
//Your actions and things to do
case R.id.example_2:
//your code
}
}
public void onClick(View $v)
{
//similar to above
}
public void initScene()
{
//initialize object and other activities related to the 3d container
}
#Override
public void updateScene()
{
//update effects such as rotation on the object here, based on the button click or touch, etc.
}
}
And the xml file containing the layout/activity should look something like this :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
//your data
android:id="#+id/example_1"
/>
<Button
//your data
android:id="#+id/example_2"
/>
<RelativeLayout
//nested layout which will hold the 3d container
android:id="#+id/SceneHolder">
</RelativeLayout>
</RelativeLayout>
for understanding more about working in initScene() function, you can refer here and here.
You can also add extra effects, by playing around with the lighting properties, as stated here
The basic example for adding into the layout was taken from here . The same page also provides links to many examples in case you want to work with in future.
My code is as followed thus far, as I am testing it out:
public class TestActivity extends Activity {
TableLayout table;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
table = (TableLayout)findViewById(R.id.table);
setStuff();
setStuff2();
setStuff3();
}
public void setStuff() {
LayoutInflater inflater = LayoutInflater.from(this);
TextView view = (TextView)inflater.inflate(R.layout.some_text, table, false);
view.setText("First");
view.setTag(String.valueOf(table.getChildCount()));
view.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
v.startDrag(ClipData.newPlainText("index", v.getTag().toString()), new View.DragShadowBuilder(v), null, 0);
Toast.makeText(v.getContext(), "index you are clicking on is "+v.getTag().toString(), Toast.LENGTH_SHORT).show();
return false;
}
});
table.addView(view);
}
public void setStuff2() {
LayoutInflater inflater = LayoutInflater.from(this);
TextView view = (TextView)inflater.inflate(R.layout.some_text, table, false);
view.setText("Second");
view.setTag(String.valueOf(table.getChildCount()));
table.addView(view);
}
public void setStuff3() {
LayoutInflater inflater = LayoutInflater.from(this);
TextView view = (TextView)inflater.inflate(R.layout.some_text, table, false);
view.setText("Three");
view.setTag(String.valueOf(table.getChildCount()));
table.addView(view);
}
protected class MyDragListener implements View.OnDragListener {
#Override
public boolean onDrag(View v, DragEvent event) {
//Stuff to check the event action cases
return false;
}
}//end of MyDragListener
Now when I long press the first item, it runs correctly because I get the Toast notification. I do not see the DragShadow though, which I am suppose to see. My guess is that because it is part of the TableLayout, it might be causing this. Any ideas?
Well this is awkward, but I think I just figured out why it did not let me drag and drop the TextView. I don't really know exactly why, but I am completely guessing that startDrag() is limited to non-root level Views that are binded to other Views. So, My xml was this:
Originally
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/da_text"></TextView>
Edit
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/da_text"/>
</LinearLayout>
Hopefully this helps someone else. There aren't many questions and even less answers on Drag-n-Drop, so it won't hurt.
A TableView normally needs TableRows to fill it. If you put your TextView in a TableRow and this TableRow in your TableView you can drag and drop the TextView wherever you want, provided that you used the right code for the OnDragListeners etc. and of course you cant drag and drop a textview that is not set in a layout because there is no place where you can drop it...
In my Android application I have an activity featuring GoogleMaps. In case of notifications etc., I show a popup window. This works all quite fine. However, I also have another activity where I want to display the same information in the same way. The idea is to use the same popup window in the corresponding view (View2). The problem is that in this second activity/view the popup window does not appear and the code seems to crash at group.addView(popup, lp); (no explicit errors though; but I'm sure there's nothing null). I just don't see the essential difference between the two activities/views that might suggest why the popup windows works fine in the in the one view but not in the other. In the following I show the relevant code snippets.
Here is how I instantiate the popup in both activities. The only difference is the third parameter that refers to the ID of the parent view; a RelativeLayout in each case.
// GoogleMaps Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mapview);
[...]
this.popupPanel = new PopupPanel(this, R.layout.popup, R.id.relativeLayoutMap);
[...]
}
// View2 Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view2);
[...]
this.popupPanel = new PopupPanel(this, R.layout.popup, R.id.relativeLayoutView2);
[...]
}
This is the main code for initializing the popup and for displaying. Only the value ''parentViewID'' differs between the activities.
public PopupPanel(Activity activity, int layout, int parentViewID) {
this.activity = activity;
this.viewID = viewID;
ViewGroup parent = (ViewGroup) this.activity.findViewById(parentViewID);
this.popup = activity.getLayoutInflater().inflate(layout, parent, false);
}
public void show(boolean alignTop) {
RelativeLayout.LayoutParams lp=new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT
);
lp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
ViewGroup group = (ViewGroup) this.activity.findViewById(this.viewID);
group.addView(popup, lp); // this 'crashes' for the View2 activity
group.invalidate();
}
Finally, here are the snippets of the corresponding layouts. In both cases I refer to a RelativeLayout where I want to place my popup.
<? xml version="1.0" encoding="utf-8"?>
<LinearLayout [...]>
<RelativeLayout [...] android:id="#+id/relativeLayoutMap">
<com.google.android.maps.MapView [...]/>
</RelativeLayout>
</LinearLayout>
For activity/view View2:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout [...] android:id="#+id/relativeLayoutView2">
<LinearLayout [...] >
...
</LinearLayout>
<LinearLayout [...] >
...
</LinearLayout>
<ScrollView [...] >
<LinearLayout [...] >
...
</LinearLayout>
</ScrollView>
</RelativeLayout>
Any hints are much appreciated! I know that has been addressed in several question, but my 'problem' is that it basically works. Just only in one activity/view, and not in another. It seems that I miss something rather stupid here.
I figured out the difference, and as expected that it was something rather stupid: I forgot to put the code that displays the panel not within the runOnUiThread. I reallt should have know better by now.
runOnUiThread(new Runnable() {
public void run() {
// code for showing the popup
}
});
One side question: I got on the right track when I put the addView(...) call within a try/catch-block. Without it just hangs up there without throwing an error (in LogCat; I'm using Eclipse). Is there a way that such errors are shown by default? Thanks!
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 :).