I've an app displaying a list of presets items in a ListView.
It's possible to add, modify and remove each of these items, most often by clicking on an item (becoming selected) and then on a button to do the desired operation.
The items are then sorted and the new list is displayed, showing the result of the operation.
Rendering problems come when I keep wanting to see the initial item as selected in the new list (except in case of removing), without having to click on it, and even without operation (in case of switching between apps). So I had to use postDelay at two places in the code.
So here is my code until now, on rebuilding the list:
...
String presetId = presetsHandler.getPresetId(listIndex); // listIndex is the initial position of the selected item
presetsHandler.sortPresets();
listIndex = presetsHandler.getIndex(presetId); // New position
...
lvAdapter.setTextItems(presetsHandler.getConcatenatedDisplayPresetDataList()); // Replace all the items
listView.setAdapter(lvAdapter); // Attach the new preset list to the ListView
...
if (listIndex != LIST_INDEX_DEFAULT_VALUE) { // Need to keep initial item as selected
final long RENDERING_DELAY_MILLIS = 200;
listView.postDelayed(new Runnable() { // Delay to besure that listView is ready (after the setAdapter)
#Override
public void run() {
if (listIndex < listView.getFirstVisiblePosition() || listIndex > listView.getLastVisiblePosition()) { // Invisible
listView.setSelection(listIndex); // To see the item at position listIndex (without selecting it yet)
}
listView.postDelayed(new Runnable() { // Delay to besure that listView is ready (after the possible setSelection)
#Override
public void run() {
View view = listView.getChildAt(listIndex - listView.getFirstVisiblePosition()); // The item at position listIndex was surely visible
view.setSelected(true); // and we can make it appear as selected
}
}, RENDERING_DELAY_MILLIS);
}
}, RENDERING_DELAY_MILLIS);
}
I'd like to know if it's a good way of doing it, particularly as for the postDelayed and Runnable.
Thanks
After a moment, I found this solution, a bit stressful for the OS (10 ms between calls to the runnable), but only during the time needed to finish rendering.
The benefit of this solution is that you don't need to estimate that time lapse and it keeps the things simple.
What do you think ?
Thanks
if (listIndex != LIST_INDEX_DEFAULT_VALUE) { // Need to keep initial item as selected
final long MINIMAL_DELAY_MILLIS = 10;
final Runnable runnable = new Runnable() {
boolean isFirstTime = true;
public void run() {
if (listView.getChildCount() == 0) { // listView is not ready yet
listView.postDelayed(this, MINIMAL_DELAY_MILLIS); // Wait until ready (after setAdapter)
} else { // listView is now ready
if ((listIndex < listView.getFirstVisiblePosition()) || (listIndex > listView.getLastVisiblePosition())) { // The item at position listIndex is not visible
if (isFirstTime) {
isFirstTime = false; // Do setSelection only once !
listView.setSelection(listIndex); // To see the item at position istIndex, without selecting it yet
}
listView.postDelayed(this, MINIMAL_DELAY_MILLIS); // Wait until visible (after setSelection)
} else { // The item at position listIndex is now visible
View view = listView.getChildAt(listIndex - listView.getFirstVisiblePosition());
view.setSelected(true); // and we can make it appear as selected; There's no need to call the runnable anymore
}
}
}
};
listView.postDelayed(runnable, MINIMAL_DELAY_MILLIS); // Run it!
}
Related
I am writing a class that has certain variables that don't need to be set except in certain fringe cases. As such, I am working on writing a requestVariable() method that asks for user input to set that variable when it is needed. However, I am struggling to figure out how to wait for that input before moving on. Let me show you what I have.
SkillApplication AR_D_Atk_Def_1_app = (Unit) -> {
if (Unit.getAttackStatus() != com.codecademy.myapplication.Unit.AttackStatus.DEFENDER) {
return;
}
else {
// Make sure structuresAR is set
Unit.setStructuresAR(requestVariable( /* some parameters */ );
int X;
if (Unit.getStructuresAR() >= 5) {
X = 4;
}
else if (Unit.getStructuresAR() == 4) {
X = 3;
}
else if (Unit.getStructuresAR() == 3) {
X = 2;
}
else {
X = 1;
}
Unit.addCombatAtk(X);
Unit.addCombatDef(X);
}
};
This is a lambda function for a certain skill's application. If this skill needs to be applied, it will run this lambda function. This is one of the fringe cases where the member "structuresAR" of Unit needs to be used. It's very rarely used, and rather than having the user set it every time, I have it set in this lambda function.
VariableRequest<Integer> requestInteger = (VariableRequest<Integer>) (questionMessage, choices, layout) -> {
final Integer[] retVal = new Integer[1];
TextView questionView = new TextView(layout.getContext());
questionView.setText(questionMessage);
EditText textEntry = new EditText(layout.getContext());
textEntry.setInputType(InputType.TYPE_CLASS_NUMBER);
Button submitButton = new Button(layout.getContext());
submitButton.setText("Submit");
layout.addView(questionView);
layout.addView(textEntry);
layout.addView(submitButton);
submitButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
retVal[0] = Integer.parseInt(String.valueOf(textEntry.getText()));
}
});
};
Here's what I have written so far for that function. It sets a question, options, and submit button to a layout that updates a return value with what is in the entry box when a button is clicked.
This problem is, this just keeps going. The rest of whatever I've written will be run while the onClickListener is still there, and I don't know how to wait until that's been clicked. I'm coming from C++ knowledge where just writing cin >> variable would pause and wait for you to enter. I'm trying to replicate that with a button.
There's also other problems I can spot with this such as getting the layout from inside a static method, but I struggle to come up with another method as I'm very new to Android development.
I am working on a "Simon Says" pattern game. The player must repeat an increasingly long pattern till they mess up.
The game consists of a 2x2 grid of colors, each with a darker one behind it. By changing the alpha from 1--->0---->1. I give the effect of clicking.
Below is the code I am using to display the pattern as it increases, it's called whenever the pattern resets or increases. But currently it always skips the last value in the pattern, and if the same color is repeated it does not flash again, it simply remains dark, what am I doing wrong?
Helpful Information:
Pattern is the arraylist where I am storing the pattern the user must repeat.
getColorById returns the corresponding ImageView for each color [0-3]
TLDR:
I need to flash every single item in an arrayList called pattern, so the player can see it and repeat it, yet currently it skips the last item and if the same item is repeated it simply stays flashed versus flashing twice.
EDIT: Sometimes, the last item in the sequence does show, but it is very random on whether it shows or not.
public void flashPattern(View view){
final Queue<Integer> queue = new LinkedList<>(pattern);
final int timeBetweenSteps = 500;
new CountDownTimer(pattern.size() * timeBetweenSteps, timeBetweenSteps) {
private ImageView lastLight = null;
#Override
public void onTick(long millisUntilFinished) {
if (lastLight != null) {
lastLight.animate().alpha(1f).setDuration(250);
}
lastLight = getColorById(queue.remove());
lastLight.animate().alpha(0f).setDuration(250);
}
#Override
public void onFinish() {
if (lastLight != null) {
lastLight.animate().alpha(1f).setDuration(250);
}
}
}.start();
}
I'm trying to find different ways of selecting multiple items in a ListView. The GUI will be running on a touch screen monitor, So I won't be able to CTRL+Click. From researching through various past posts, I have been able to implement Multiple Selection via keeping all the selected items in an Array and then looping through it to get the final selections. The only problem I have with my code is that compared to a CTRL +click , the selection is done smoothly, where as my code leads to a type flickering every time a new item is selected. So basically the listView clears all the selections and then selects the correct ones. Is there a way to make this transition go smoothly? Would it be easier to mimic a touch to have CTRL+click effect?
selectedList = new int[totalTypes];//total number of item properties
for(int x=0; x<selectedList.length;x++){//0 = not selected, 1 = selected
selectedList[x]=0;
}
testView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
testView.setOnMouseClicked(new EventHandler<Event>(){
#Override
public void handle(Event event){
if(selectedList[testView.getSelectionModel().getSelectedIndex()]==0){
selectedList[testView.getSelectionModel().getSelectedIndex()]=1;
}
else{
selectedList[testView.getSelectionModel().getSelectedIndex()]=0;
}
for(int x=0; x<selectedList.length;x++){
if(selectedList[x]==1){
testView.getSelectionModel().select(x);
}
else{
testView.getSelectionModel().clearSelection(x);;
}
}
}
});
You could handle changing the selection when a user clicks a ListCell yourself instead of using the standard event handling:
#Override
public void start(Stage primaryStage) {
ListView<Integer> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
listView.getItems().setAll(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
listView.addEventFilter(MouseEvent.MOUSE_PRESSED, evt -> {
Node node = evt.getPickResult().getIntersectedNode();
// go up from the target node until a list cell is found or it's clear
// it was not a cell that was clicked
while (node != null && node != listView && !(node instanceof ListCell)) {
node = node.getParent();
}
// if is part of a cell or the cell,
// handle event instead of using standard handling
if (node instanceof ListCell) {
// prevent further handling
evt.consume();
ListCell cell = (ListCell) node;
ListView lv = cell.getListView();
// focus the listview
lv.requestFocus();
if (!cell.isEmpty()) {
// handle selection for non-empty cells
int index = cell.getIndex();
if (cell.isSelected()) {
lv.getSelectionModel().clearSelection(index);
} else {
lv.getSelectionModel().select(index);
}
}
}
});
Scene scene = new Scene(listView);
primaryStage.setScene(scene);
primaryStage.show();
}
when I run my app it will continuously be stuck in a while loop. It should leave the while loop when a button is pressed, however even after pressing a button it continues to loop.
View.OnClickListener listener11 = new View.OnClickListener() {
#Override
public void onClick(View view) {
temp1 = button[0];
temp3 = button[0].getBackground();
state++;
}
};
while(state == 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
button[0].setOnClickListener(listener11);
}
variable:
state is an int temp1 is a button temp3 is a Drawable
the delay is there because I actually have 20 buttons and I have the setOnClickListeners for all the buttons inside that while loop so I think it causes it to crash without the delay. Another question would be is it possible to have the setOnClickListeners outside the while loop but still be able to check for button clicks inside the loop?
The problem is you are pressing the button while the thread is in a sleeping state causing the event to not be triggered and therefor the state to never change.
You should remove the while loop and just set all the listeners in a for loop:
for(int i = 0; i< button.length; i++) {
button[0].setTag(i);
button[0].setOnClickListener(listener11);
}
And then change your listener to be something like:
private boolean firstClick = true;
View.OnClickListener listener11 = new View.OnClickListener() {
#Override
public void onClick(View view) {
if(firstClick) {
firstClick = false;
temp1 = button[(int)view.getTag()];
} else {
temp2 = button[(int)view.getTag()];
}
}
};
I'm not sure exactly what the code is supposed to be doing because I have no idea of the context, but I think think you could remove the while loop altogether:
// initialize the listener
View.OnClickListener listener11 = new View.OnClickListener() {
#Override
public void onClick(View view) {
// add code that runs when button is clicked here
}
};
// now that we have the listener all set up we add it to the button, at which point it just keeps listening for you, no need to put the thread to sleep
button[0].setOnClickListener(listener11);
UPDATE:
So because you want to see if the second value matches the first value you could use a helper method:
private String firstValue = "";
public boolean isMatch(String mostRecentValue) {
boolean match = false;
if (firstValue.isEmpty()) {
firstValue = mostRecentValue;
} else {
match = firstValue.equals(mostRecentValue);
firstValue = "";
}
return match;
}
This method takes a value and if it is the first click the value is saved, if it is the second click it compares it with the first click and then resets the method. A boolean is returned false for no match, and true for a match. Call this method from within the onClick() method and pass in the buttons value. I've used String as an example value but you could use any object type.
I think you might be misunderstanding what setOnClickListener is doing. It's not handling the click, it is setting up the click handler. The line button[0].setOnClickListener(listener11); would be part of your activity initialisation and then not called again.
The reason you get stuck is because the main thread is busy in the while loop and isn't given an opportunity to process click events. Click events are handled on the main thread but only when it's not already busy doing something. In this case, it's forever busy in the while loop. For example, if the main thread was processing a method that would take 10 seconds to complete and you tap on a button 3 seconds into it, the button press wouldn't be handled for another 7 seconds. At which point, the previously set listener11 would be executed.
What you seem to be trying to achieve is already handled by the Looper. You need to be thinking in terms of event handling. So in other words, all your logic needs to go in listener11 or something similar.
Apparently the notifyDataSetChanged() only updates the visible items in the listview, I have a system that changes the background color of an item when its clicked (to dkgray), and I set everything else to transparent(the default), however other items that aren't visible remain selected(dkgray) (I only want the currently selected item to be dkgray). Is there a way to force notifyDataSetChanged() to update all items.
Here's example:
//makes all item backgrounds transparent
public void resetListViewBackground(){
for (int i = 0; i < listView.getChildCount(); i++){ //parent.getChildCount()
listView.getChildAt(i).setBackgroundColor(Color.TRANSPARENT);
}
}
//reloads the listview
private void reloadListView() {
listItems.clear();
adapter.notifyDataSetChanged();
listView.invalidateViews();
ArrayList<HashMap<String, String>> notesArrayList = dbTools.getAllNotes();
for (int i = 0; i < notesArrayList.size(); i++){
String temp = "";
if (notesArrayList.get(i).get("note").length() > 51){
temp = notesArrayList.get(i).get("note").substring(0,50).toString() + "...";
} else {
temp = notesArrayList.get(i).get("note").toString();
}
listItems.add(temp);
adapter.notifyDataSetChanged();
}
}
Everywhere I call resetListViewBackground(), I call reloadListView() after.
And this is what I use to highlight the selected item.
view.setBackgroundColor(Color.DKGRAY);
Also, the most common occurrence of this problem is that every 6th item is highlighted. The listview only shows about 4 items at a time.
The getChildCount() method won't work for all list items as view recycling is done.
Astral is right as he writes in the comments.
1) Create a custom adapter.(See http://windrealm.org/tutorials/android/listview-with-checkboxes-without-listactivity.php)
2)Inside the onListItemClick listener's onItemClick() method, call your adapter's notifyDataSetChanged() method(whenever the user clicks a list item).
I modified the project that I mentioned and posted it on my Dropbox.(Just import it in Eclipse and run).
Check it out at https://www.dropbox.com/s/gchccjzpkxk8n2z/Planets_modified.zip