I have an application that should work on a tablet, and there's a page where a recyclerview is present and in its row item there is 2 edit texts (with numberDecimal as input), the default keyboard shouldn't appear because it's covering a considerable large portion of screen.
I created buttons in the activity to act like the keyboard buttons however the problem is how to make the button from activity to communicate with the edit texts in the adapter.
how can i know that if I press (Button "1") for example that it should display 1 in the focused edittext in the adapter, and how if I pressed "<" or ">" button it should know the previous and next edit texts
please help
I'll provide example in Kotlin, I assume that is what you are using.
So you can simply override the key handling in the parent class like:
Make a method that you call one time from onCreate like:
fun keydown_onClick(){
try{
myActivity.setOnKeyListener { _, keyCode, event ->
if (keyCode == android.view.KeyEvent.KEYS_YOU_CARE_ABOUT) {
//handle it
if(activeViewHolder != null){
var displayText = activeViewHolder.yourEditText.text
keyPressedChar = (char)event.getUnicodeChar()+""
//if it's a special key you care about, then handle it in a when statement or if and pass to your adapter if needed to go to next row for example.
displayText += keyPressedChar
activeViewHolder.yourEditText.text = displayText
}
return#setOnKeyListener true//we've processed it
} else
return#setOnKeyListener false// pass on to be processed as normal
}
}catch (ex: Exception){
A35Log.e(mClassTag, "Error handling onkeydown listener: ${ex.message}")
}
}
Next up is how do you handle it. well you should keep track of the active row. You can do this by creating a callback in your activity that gets notified when a different row is selected or gains focus.
interface IFocusChangeListener {
fun onNewItemFocused(holder: MyApdaterViewHolder, item: DataItem, index: Int)
}
This will be passed into your adapter and used to fire back to your activity class.
//in activity
var activeViewHolder: MyAdapterViewHolder? = null
var activeIndex: Int? = null
var activeItem: DataItem? = null
fun onNewItemFocused(holder: MyAdapterViewHolder, item: DataItem, index: Int){
activeViewHolder = holder
activeIndex = index
activeItem = item
}
//now in your key down event you simply pass through the value to the editText in the activeViewHolder,
So last piece is in the adapter.
//on bind View
create View holder, the usual bloat.
Then when you have your editText you simply add.
var item = currentDataItem
var index = currentDataItemIndex
var viewHolder = currentViewHolder //from onBind
viewHolder.setOnFocusChangeListener(object: View.OnFocusChangeListener{
override fun onFocusChange(v: View?, hasFocus: Boolean) {
if(hasFocus){
mFocusListener?.onNewItemFocused(viewHolder, item, index)
}
}
})
//in adapter you may also need
fun onNextEditTextClicked(item: DataItem, index: Int){
//get next data Item by index + 1 if exist and get it's viewholder.editText and set focus to it, this will automatically trigger the focus event back to the activity
}
fun onPreviousEditTextClicked(item: DataItem, index: Int){
//get next data Item by index - 1 if exist and get it's viewholder.editText and set focus to it, this will automatically trigger the focus event back to the activity
}
Now you have the focused viewholder in your calling activity, you can catch the keys you care to catch, probably all of them. You can pass them in, you should be good to go.
NOTE*
For the record, if you are using modern practices, aka Data Binding, then you should be able to just update a bindable string in your model and it will show up on the screen without having to pass it around. You could also bind the focus to being selected and just update the selected boolean of the models. There are cleaner ways to do this if you use binding. But for now, just helping you without complicating it.
This solution may need tweaked, I just typed it here so could be off a bit, but should mostly get you there.
Happy Coding.
Intiliased the Adapter before clicking the buttons. because, required the instance of your Adapter to perform.
create a method add() in your Adapter
ArrayList<String> dataList = new ArrayList<>();
public void add(String element) {
dataList.add(element);
notifyItemInserted(dataList.size() - 1);//this will update the recyclerview with data inserted
}
from your button click call this method with your Adapter instance
yourAdapter.add(btn1.getText().toString());
Related
I am using cell factory for listview with checkboxes like:
listView.setCellFactory(CheckBoxListCell.forListView(new Callback < Bean, ObservableValue < Boolean >> () {
#Override
public ObservableValue < Boolean > call(Bean item) {
BooleanProperty observable = new SimpleBooleanProperty();
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
if (!beanChoices.contains(item.toString())) {
beanChoices.add(item.toString());
observable.setValue(true);
//listView.scrollTo(listView.getItems().size() - 1);
}
} else if (wasSelected) {
if (beanChoices.contains(item.toString())) {
beanChoices.remove(item.toString());
observable.setValue(false);
}
}
});
/* [Code] which compares values with bean item string value and select observable to true for that for edit mode
but here the observer not called for beanItem that are under scrollpane of listview. But on scroll it gets called. */
return observable;
}
}));
It works fine but not for all cases.
Case: When I have say more than 10 entries, the scrollpane comes. Say I have beanChoices to be checked that are at 8 or 9 index(you have to scroll to view them). The listener is not called for the items not visible(that are under scrollpane). On Debug, I found that listener is called when I scroll down.
Problem: when I get checked values from beanChoices for above case, it return empty.
Detail: I have beanChoices which I need to make checked for listview items (edit mode). When I update without changing anything. (Assume that the value which is under the scrollpane of listview will be selected and added to beanChoices)
The Callback is used to retrieve the property for the checked state when the item is associated with a cell. The item may be removed from a cell and put in a new one at any time. This is how ListView (and similar controls like TableView) works. CheckBoxListCell simply gets the checked state property every time a new item is associated with the cell.
The return value is also used to set the initial state of the CheckBox. Since you do not properly initialize the property with the correct value the initial state is not preserved.
Also note that it makes little sense to update the value of the property to the new value in the change listener. It happens anyway.
Since BooleanProperty is a wrapper for primitive boolean the possible values are true and false; the ChangeListener only gets called when !Objects.equals(oldValue, newValue) you can be sure that isNowSelected = !wasSelected.
Of course you also need to return the value:
#Override
public ObservableValue < Boolean > call(Bean item) {
final String value = item.toString();
BooleanProperty observable = new SimpleBooleanProperty(beanChoices.contains(value));
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
beanChoices.add(value);
} else {
beanChoices.remove(value);
}
});
return observable;
}
I also recommend using a Collection of Beans instead of relying on the string representation of the objects. toString many not produce unique results and Beans.equals would be the better choice to compare the objects.
I am using dhtmlxgrid but my first row doesn't get selected upon using onRowSelect but works fine upond using the event onRowDblClicked or I use keyboard navigation. But upon using keyboard nagivation and navigating to the first row, the navigation gets stuck and I cannot navigate from the first row using the keyboard but clicking on a different row works fine. Here's my code:
function onGraphPropGridRowSelect(id)
{
// push the previously selected graph's data to the JSON array object
if(lastSelectedGraphIndex != -1)
{
if(validateGraphProperties())
{
// Note: GraphType is uneditable, so just copy existing value
pushGraphPropertiesToJSONArr(lastSelectedGraphIndex,
graphPropertiesJson[lastSelectedGraphIndex].GraphType);
}else
{
definedGraphGrid.selectRow(definedGraphGrid.getRowIndex(lastSelectedGraphIndex));
return false;
}
}
// now populate the newly selected graph data to the UI elements
populateUIElementsForSelectedGraph(id);
// update the lastSelectedGraphIndex to the newly selected graph index
lastSelectedGraphIndex = id;
if (id==0){
definedGraphGrid.enableKeyboardSupport(true);
alert("first row selected");
}
}
Unfortunately it is not available to use the "0" as an ID of the row. Please, try to use another id.
I have a Toolbar and an item (add) which, when clicked, adds a view in listView below. However, the onOptionsItemSelected gives you the effect of a single click so it only adds one view, and in my case, I need multiple views, thus multiple clicks are required. How do I set up everything so that the item behaves as an onClickListener rather than a single click?
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.addButton){
final TextView noProject = (TextView) findViewById(R.id.NOPROJECT);
final ArrayList<String> listItems=new ArrayList<String>();
final ListAdapter addAdapter = new ArrayAdapter<String>(this,
R.layout.list_item, R.id.listFrame, listItems);
final ListView lv = (ListView) findViewById(R.id.lv);
lv.setAdapter(addAdapter);
noProject.setVisibility(View.GONE);
lv.setVisibility(View.VISIBLE);
listItems.add("New Project");
((ArrayAdapter) addAdapter).notifyDataSetChanged();
}
if (id == R.id.addPeople) {
return true;
}
return super.onOptionsItemSelected(item);
}
Android is always listening for menu item clicks. And on click your action will happen, so you'll need to click multiple times anyways if you want this add feature in the menu.
I usually setup my list adapter in onCreate or onCreateView. Once it's established you can do addAdapter.clear() and addAdapter.add(item). You shouldn't need to reference your listitems directly since the ArrayAdapter.add() method is setup to append to that list anyways and then if i'm not mistaken you can get rid of notifyDataSetChange() - I've never had to use this method with any of the default list adapters or the custom adapters I've written. .clear(), .add(), .insert(), and .remove() should be sufficient.
My listview is usually filled out using a for loop. If you want multiple views added then could you just setup a loop instead of waiting/requiring for more clicks?
Maybe I'm not fully understanding the usecase but a basic for loop seems like the answer here.
Edit:
//For Each Loop - "For each individualItem in itemHolder"
listadapter.clear();
for(ItemType individualItem : itemHolder){
listAdapter.add(individualItem.getText());
}
or you can do a traditional for loop
//"For i(index) starting at index 0, run until index < itemHolder.getItemCount() is false"
//for(initialize index variable : condition check : increment after each iteration)
for(int index =0; index<itemHolder.getItemCount(); index++)
{
listAdapter.add(itemHolder.getItemAt(index));
}
Something like that. I made up method names obviously it's going to depend on your data structures.
I am using the JavaFX Combobox for the first time... and I have over 2000 icons in the combobox (they can be filtered via AutoCompleteComboBoxListener that I found from StackOverflow).
I am planning to use the ExecutorService to fetch the images from a zip-file.
Now, the problem is that how can I figure out the currently visible items in the Combobox?
I am setting a custom ListCellFactory for the ComboBox, and I have a custom ListCell class, that also displays the icon. Can I somehow check from within the ListCell object whether the item "is showing" ?
Thanks.
Just note first that if you are loading the images from individual files instead of from a zip file, there is a mechanism that avoids having to work directly with any kind of threading at all:
ComboBox<MyDataType> comboBox = new ComboBox<>();
comboBox.setCellFactory(listView -> new ListCell<MyDataType>() {
private ImageView imageView = new ImageView();
#Override
public void updateItem(MyDataType item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
String imageURL = item.getImageURL();
Image image = new Image(imageURL, true); // true means load in background
imageView.setImage(image);
setGraphic(imageView);
}
}
});
Unfortunately, if you're loading from a zip file, I don't think you can use this, so you'll need to create your own background tasks. You need to just be a little careful to make sure that you don't use an image loaded in the background if the item in the cell changes during the loading process (which is pretty likely if the user scrolls a lot).
(Update note: I changed this to listen for changes in the itemProperty() of the cell, instead of using the updateItem(...) method. The updateItem(...) method can be called more frequently than when the actual item displayed by the cell changes, so this approach avoids unnecessary "reloading" of images.)
Something like:
ExecutorService exec = ... ;
ComboBox<MyDataType> comboBox = new ComboBox<>();
comboBox.setCellFactory(listView -> {
ListCell<MyDataType> cell = new ListCell<MyDataType>() ;
ImageView imageView = new ImageView();
ObjectProperty<Task<Image>> loadingTask = new SimpleObjectProperty<>();
cell.emptyProperty().addListener((obs, wasEmpty, isNotEmpty) -> {
if (isNowEmpty) {
cell.setGraphic(null);
cell.setText(null);
} else {
cell.setGraphic(imageView);
}
});
cell.itemProperty().addListener((obs, oldItem, newItem) -> {
if (loadingTask.get() != null &&
loadingTask.get().getState() != Worker.State.SUCCEEDED &&
loadingTask.get().getState() != Worker.State.FAILED) {
loadingTask.get().cancel();
}
loadingTask.set(null) ;
if (newItem != null) {
Task<Image> task = new Task<Image>() {
#Override
public Image call() throws Exception {
Image image = ... ; // retrieve image for item
return image ;
}
};
loadingTask.set(task);
task.setOnSucceeded(event -> imageView.setImage(task.getValue()));
exec.submit(task);
cell.setText(...); // some text from item...
}
});
return cell ;
});
Some thoughts on performance here:
First, the "virtualized" mechanism of the ComboBox means that only a small number of these cells will ever be created, so you don't need to worry that you're immediately starting thousands of threads loading images, or indeed that you will ever have thousands of images in memory.
When the user scrolls through the list, the itemProperty(...) may change quite frequently as the cells are reused for new items. It's important to make sure you don't use images from threads that are started but don't finish before the item changes again; that's the purpose of canceling an existing task at the beginning of the item change listener. Canceling the task will prevent the onSucceeded handler from being invoked. However, you will still have those threads running, so if possible the implementation of your call() method should check the isCancelled() flag and abort as quickly as possible if it returns true. This might be tricky to implement: I would experiment and see how it works with a simple implementation first.
Even if your list has 2000 items javafx will only create listcell objects for the visible cells (plus one or two more for half visible cells) so there's not really a lot TODO for you to load Images lazy - just load them when updateItem is called - and maybe cache already loaded Images in a lifo Cache so that not all of them stay in memory
Current visible item implies the current selected item on combobox. You can get the selected item using
comboboxname.getSelectionModel().getSelectedItem();
//first i have this method , below is my question
public void addrows(){
TableRow fila;
tabla = (TableLayout)findViewById(R.id.tabla);
TextView txtNombre;
for(int i = 0;i<id;i++){
String x[] = helper.leer();
layoutFila = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT);
caja= new CheckBox(this);
fila = new TableRow(this);
fila.setLayoutParams(layoutFila);
txtNombre = new TextView(this);
txtNombre.setId(i);
txtNombre.setTextSize(17);
txtNombre.setText(x[i]);
txtNombre.setGravity(Gravity.LEFT);
// txtNombre.setLayoutParams(layoutTexto);
caja.setText("");
caja.setId(i);
fila.addView(txtNombre);
fila.addView(caja);
tabla.addView(fila);
}
}
i know that when the oncreate() method start the checkboxes objects are created and then i assign an numerical id from 0 to wherever the for cycle stop , but later in the program i need to retrieve what checkboxes were clicked so first i need the id but eclipse wont let me put the numerical id, please help! and sorry for my English i'm a noob in android and the English language
this.CheckBox = (CheckBox)this.findViewById(R.id.?);
As You may read in View class documentation ID should be unique within a tree You search.
You set same id for TextView and Checkbox.
If You know You are going to access them all later after creation keep references to them in array instead of trying to retrieve them later using findViewById.
But even better solution would be to set onClick event listener for them and keep track of checking/unchecking them.
In #HalR's answer You may read how to set onCheckedChanged event listeners for Your checkboxes. Folowing his solution will have an ArrayList of checked checkboxes.
Next step, You have to increment values of correct TextView so You need to couple CheckBoxes and TextViews.
I think best for this would be to set Tag for CheckBox with value of TextView id.
So after user submits You iterate over List of checkboxes, getTag and use it in findViewById to get TextView and update its value.
Id (short for IDentifier) is an integer to uniquely identify elements, You can use it in findViewById to get view elements. You can read more about ID in this answer
Tag is used to associate View element with some extra data as You may read in getTag documentation. It takes as parameter Object type so You set as tag anything not only numbers. In Your case You could set as ChechBox's tag a TextView instead of its id and it will work too.
You are manually setting your id to the index of the row, which is something I don't think I'd do. I'd normally use setTag() to identify my object.
I think it would be easier to use a listener to detect when the checkboxes have been checked, and you can track the changes when the check happens.
use something like this:
In your Activity, create a ArrayList
ArrayList<CheckBox> checkedBoxes = new ArrayList<CheckBox>();
then in your creation:
caja= new CheckBox(this);
caja.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
int index = buttonView.getId();//pulling back the number you set originally, if that is what you like. I would get the tag I had set, and maybe do something here.
//do something in here!
if (buttonView.isChecked()) {
//including this checkbox in the list of checked boxes
checkedBoxes.addObject(buttonView);
} else {
//take it out of the list of checked boxes
checkedBoxes.removeObject(buttonView);
}
}
};
Some info on Id vs Tag
Id is a numeric value that identifies the view in the view hierarchy. If you are using things in your layout, like aligning one view with another, they look for and expect a view with a specific id. So in layout, you'll have android:id="#+id/bigBox" and that will create some number that it associates with bigBox. When you find that view, with findViewById() that is the number it is looking for. When you manually set those numbers, it seems like you are asking for trouble. If you set a view's id to 2, then you should be able to find it with myView = findViewById(2).
Tag is a nicely little object pointer that you can pass along with your view. Quite often it will be a row number:
Integer saveMe = new Integer(i);
checkBox.setTag(saveMe);
Or it can even be a pointer to your original data object that you used to create that row. If you had created each row using a contact, you could use
myRow.setTag(contact)
and later when you clicked on that row, you would just use
contact = (Contact)myRow.getTag()
and you would have your original contact back. Its way cleaner than keeping big arrays of your rows or checkboxes, or whatevers. Just use listeners that detect when you do something, that is a much better way.
Oh, and if you if you do have an onClick(View view) that is triggered by your CheckBox, that view IS your CheckBox.
CheckBox theBoxIJustChecked = (CheckBox)view;
You don't need to look it up with some id. It's right there.
If you want to go this way than you should just do the apposite operation i.e.:
for(int i = 0; i < n; ++i){
...
...(CheckBox))this.findViewById(i);
...
}
It should work for you
However be careful as if you have number of views with the same id inside the view-tree than findViewById(i) can return an unexpected result such as returning the first view in view-tree with given id (it can be not of CheckBox type which can lead to ClassCAstException)
Update in reply to comment
If you want to make some sort of logical connection CheckBox-TextView there are several options:
You can make a sort of function like the following (assuming that there is the limit of CheckBoxes and TextViews quantity):
Code:
private static int CHECK_BOX_MAX_NUMBER = 10000;
public void int getTextVieIdByCheckBoxId(int checkBoxId){
if(checkBoxId >= CHECK_BOX_MAX_NUMBER){
// you can throw an exception here for example
}
return CHECK_BOX_MAX_NUMBER + checkBoxId;
}
And then you should set id's to your TextViews with that function.
checkBox.setId(i);
textView.setId(getTextVieIdByCheckBoxId(i));
....
// add Views to your layout
....
(CheckBox)this.findViewById(i);
TextView)this.findViewById(getTextVieIdByCheckBoxId(i));
or
2.I think there is a little bit more accurate method:
Just use setTag() of CheckBox instances to set appropreate TextView inside in order to create interconnection. In thiscase you have to store all the created checkBoxes in some List or array:
List<CheckBox> checkBoxList = new ArrayList<CheckBox>();
for(int i = 0; i < n; ++i){
...
CheckBox checkBox = new CheckBox();
TextView textView = new TextView();
checkBox.setTag(textView);
checkBoxList.add(checkBox);
}
Then you can achieve what you want like this:
int textBoxListSize = checkBoxList.size();
for(int i = 0; i < textBoxListSize; ++i){
CheckBox checkBox = checkBoxList.get(i);
if(chechkBox.isChecked()){
TextView textView = (TextView)checkBox.getTag();
//do whatever with textView
}
}
Here you don't need to generate id's and worry about collisions which could accure