I have a Recyclerview where there is a button onclicklistener in the viewholder constructor:
public ViewHolder(View itemView)
{
super(itemView);
...
button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
...
}
}
}
Each item also has a countdown, every second the countdown is decremented and a progressbar of the item is updated to display the time left.
The timer is done with a runnable:
countdownHandler.postDelayed(countdownRunnable, 1000);
which then iterates through all the items, decrements the countdown and notifies the adapter:
countdownRunnable = new Runnable()
{
#Override
public void run()
{
Iterator<Item> iterator = countdownTasks.iterator();
while (iterator.hasNext())
{
item.decrementCountdown();
adapter.notifyItemChanged(items.indexOf(item));
}
if (countdownTasks.size() > 0)
countdownHandler.postDelayed(countdownRunnable, 1000);
}
}
Here is my problem, it also occurs if there is only one item in the list.
I press the button and is in the pressed state, but when the countdown update triggers, the button is no longer pressed. Releasing the button doesn't activate the button as it should. If I remove the countdown, it works properly.
I don't change the button in onBindViewHolder and I am not scrolling since there is only one item. Is this expected bahavior, that all button presses get canceled as soon as notifyItemChanged is called or am I doing something wrong? Does this even work with the onClickListener or do I need an onTouchListener and save the touchdown state in the item and reset it every update?
Thank you!
Edit:
I found out, that if I set
recyclerView.setItemAnimator(null);
it works without problems. Does anyone know why that is the case and how I can still get animation without the hassle of remembering all the states?
When you call adapter.notifyItemChanged(items.indexOf(item)); it rebind view for that position, which is fresh view stored in ViewHolder & don't have a pressed state & that's why it loses button state.
You can do it by using OnTouchListener and storing states as you mentioned in question.
I am writing as anshwer since i don't have reputation for comment.
Related
I have a listview and in each cell it has a RelativeLayout with 7 buttons.
before the list is scrolled all the buttons work fine (all trigger when clicked) for all visible listView items, but after listView was scrolled some items turn to not clickable (no matter which button in the item I click), and it's random, after another scroll the same item can turn clickable, and other which was before turns to not clickable.
I have noticed that it usually happens (item turns not clickable) after scrolling all the way up.
Another thing that i have noticed that seldom (after 4-5 unsuccessful clicks in a row) the button triggers a few times in a row (like it was delayed). But usually it's not happening after a number of unsuccessful clicks.
In my original code I created an arrayList of RelativeLayouts (each for listView Item), and put the arrayList into adapter. For every 7 buttons (for each cell) I set 7 ids corresponding to their's place in arraylist.
In that way I implemented the OnClick event in the main class.
Here is 3 buttons (out of 7):
for (int i = 0; i < EXPEND_BUTTONS.length; i++) {
if (view.getId() == EXPEND_BUTTONS[i]) {
handleEmojiPanel(i);
break;
}
if (view.getId() == BUTTONS[i] || view.getId() == IMAGES[i]) {
ShowTopItem item = new ShowTopItem(getActivity(), i);
item.show();
break;
}
}
Because of the problem I change the code.
I handled the OnClick event for the buttons in the adapter itself in the getView method (for 2 buttons only):
public View getView(final int position, View convertView, ViewGroup parent) {
pos = position;
Button btn = (Button) listOfObjects.get(position).getChildAt(0);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ShowTopItem item = new ShowTopItem(getActivity(), position + listChosen);
item.show();
}
});
Button imageBtn = (Button) listOfObjects.get(position).getChildAt(2);
imageBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ShowTopItem item = new ShowTopItem(getActivity(), position + listChosen);
item.show();
}
});
return listOfObjects.get(position);
}
I have the same result. Nothing changed.
I have looked all over the internet, and it seems that I'm the only one who encountered such issue.
Id anybody knows what can be the issue here?
If some other code is needed, please feel free to ask.
I did not find the reason, but I changed ListView to ScrollView, and all works fine now.
Maybe there is some kind of bug in ListView, but in this case, I wonder why I did not find any complains regarding it.
Anyway, works perfect with ScrollView.
public class TwoPlayers extends AppCompatActivity implements
View.OnClickListener {
private Button start, start2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.two_players);
start = (Button) findViewById(R.id.buttonStart);
start2 = (Button) findViewById(R.id.buttonStrat2);
start.setOnClickListener(this);
start2.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if((v == start)&&(v == start2)){
start.setVisibility(View.GONE);
start2.setVisibility(View.GONE);
...
Initially I thought this will be very easy to do since all phones support multi-touch for years now, but now that I red around it seems it's harder to do than I tought, I need to press 2 buttons simultaneously to start a game. My approach with onClick listener above doesn't work. What will be the easiest way to do this ? Because the approach I found so far involves using OnTouchListener and ACTION_DOWN, and then record some coordinates, and check if the coordinates are within button area, which is kind of complex. Not only that but all my other buttons are using onClick and if I use just for starting the game onTouch will I have to use it for all the other buttons as well or I can leave them using onClick ?
The condition if((v == start)&&(v == start2)) can never be true. v cannot have two different values at the same time.
The onClick method will be called twice, one time when you press the start button and another time when you press the start2 button.
You can use the isPressed method to check when the views are pressed at the same time (Button is a View, so it inherits all its methods).
In other words:
#Override
public void onClick(View v) {
if(start.isPressed() && start2.isPressed()) {
start.setVisibility(View.GONE);
start2.setVisibility(View.GONE);
...
When the first button is pressed, onClick is called but the condition is false. When the second button is pressed (and the first one is still pressed) the condition will be true.
I have a simple CountDownTimer that displays its content in a textView. I am trying to accomplish the following:
during count down, the view is not clickable: textview.setClickable(false);
after count down finish, if the user clicks the textview, the count down should restart.
So I try a combination of
#Override
public void onFinish() {//inside CountDownTimer
view.setClickable(true);
}
and
textview.setOnClickListener(countAgain);
OnClickListener countAgain = new OnClickListener() {//inside activity
#Override
public void onClick(View v) {
// counter.cancel();
counter.start();
}
};
But this is not doing it. Any ideas?
Set onclick listener for that text view, make it clickable .
In onclicklistner, make textview as non-clickable( yourTextView.setclickable(false) ).
change the text view values as you want and every time check the value against 0. When it becomes zero, make the text view clickable( yourTextView.setclickable(true) ).
Hi i have two textViews that i initially set its visibility to gone then animate in and become visible. now i want to make the invisible again but for some reason they're still showing on screen does anyone no why?
in my onCreate() i make the view gone
register = (TextView)findViewById(R.id.register);
register.setVisibility(View.GONE);
forgotpassword = (TextView)findViewById(R.id.forgotpw);
forgotpassword.setVisibility(View.GONE);
then later on i make it visible
public void run()
{
animations();
loginForm.setVisibility(View.VISIBLE);
register.setVisibility(View.VISIBLE);
forgotpassword.setVisibility(View.VISIBLE);
}
and then when a user presses a button i want the text views to become invisible so that they retain their layout but they stay visible on screen
signInBtn = (Button) findViewById(R.id.signin);
signInBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
signInProcess();
}
});
public void signInProcess() {
register.setVisibility(View.INVISIBLE);
forgotpassword.setVisibility(View.INVISIBLE);
setuploader.setVisibility(View.VISIBLE);
}
In Android when you animate something, It's just drawn somewhere else. The actual element is not moved. So when you animate signInBtn it's drawn somewhere else, but the actual button is not moved from the original position. So when you click the button the click handler is not called.
To avoid this set fillAfter = True in your animation so the button will actually get moved at the end of your animation.
Also, after animating a view in Android make sure you call View.clearAnimation() before trying to change its visibility.
I'm having a problem when setting the visibility of two image buttons one on top of the other. The idea is to implement a play/pause control. The problem is that the only part where setting the visibility actually works is in the click listeners of the buttons. If I try to change it somewhere else nothing happens. Any idea why is this happening?
playBtn.setOnClickListener(new OnClickListener() {//PLAY BUTTON LISTENER
public void onClick(View v) {
playBtn.setVisibility(ImageButton.GONE);
pauseBtn.setVisibility(ImageButton.VISIBLE);
mp.start();
}});
pauseBtn.setOnClickListener(new OnClickListener() {//PAUSE BUTTON LISTENER
public void onClick(View v) {
pauseBtn.setVisibility(ImageButton.GONE);
playBtn.setVisibility(ImageButton.VISIBLE);
mp.pause();
}});
final class SeekBarTask extends TimerTask {
public SeekBarTask(int duration) {
}
#Override
public void run() {
if(seekBar.getProgress() >= mp.getDuration()) {//IF SONG HAS FINISHED...
pauseBtn.setVisibility(ImageButton.GONE);//THESE ONES
playBtn.setVisibility(ImageButton.VISIBLE);//DOESN'T WORK
mp.stop();
}
else {
seekBar.incrementProgressBy(100);
}
}
}
I would recommend just changing the icon of one ImageButton.
I would think only one of two things could be happening. Either this code never gets hit, or the variables are not referring to the same object instances you're expecting them to. Have you put a breakpoint inside that condition? I would check that a break point even gets hit in there, and then check that the variables are pointing at the correct button instances.
Without seeing the rest of the code I have to ask...why are you checking on a progress bar for a "finished playing" condition versus using the media players on completion callback?
I'm doing something very similar, and I use the MediaPlayer's OnCompletionListener to flip the visibility of my buttons.
I don't remember the details of Android GUI manipulation but could it have to do that you're doing it from another thread and you're not supposed to?
i noticed that setting an ImageButton to View.INVISIBLE is not working when you have set an Animation to it. you have to remove the Animation then make it Invisible. bad pitfall i think...