In my custom View, I'm drawing a rectangle with an alpha of 0. I want that when the user clicks a button, the alpha would change to 100 and redraw. When the mouse button is depressed, alpha should reset to 0. From what I can see, it is not redrawn as 0, but rather on every click it adds 100 without ever going to 0.
if(transparentRightClick){
gameBasePaint.setAlpha(0);
}
else{
gameBasePaint.setAlpha(clickRightColor);
}
nextClicked = new RectF(displayWidth*0.8f,displayWidth*0.91f,displayWidth*1f,displayWidth*1.35f);
gameBaseCanvas.drawRect(nextClicked,gameBasePaint);
This is in the 'onDraw' method. When he clicks the button, the 'if'statement is set to false and 'clickRightColor' changes dynamically (0-100) to give an animation effect. When the mouse button is depressed, the 'if' is set to true again, and it should be redrawn to nothing. Calling invalidate after every action.
My problem turned out to be that the 'gameBaseCanvas' wasn't being reinitialized in the onDraw method so it kept on adding to it and never removing the previous drawings.
Related
In my project I have an expandable recycler view. Almost everything is working properly.
When I expand the view I have a chevron, that I want to perform a 180º rotation and vice versa for the collapsing animation.
I'm using this function to do the rotation view.animate().rotation(180f) for one of the states and for the other I pass 0 to the rotation function.
The icon is rotated the first time with the animation. My problem now is that, the second time I call submitList(List<UIModel>) it doesn't perform the animation itself, but the icon blinks into the 180º state.
One important note is that I'm using MVI, and I added a click listener to the ViewHolder, which is simply a callback for the Fragment, that will pass this info to the ViewModel. The ViewModel will update its state to have that list item as expanded and then submit the list.
Rotation animation works perfectly, if I call the function mentioned above right in the click listener, without calling the submitList() function.
As a summary, this is flow (first time 8th step doesn't apply, and it works properly with the animation):
User clicks on chevron;
callback is called;
fragment notifies view model;
view model updates state;
fragment is notified that there are changes;
fragment submits list calling (submitList());
onBindViewHolder is called, and I perform the rotation animation according to the UIModel isExpanded variable.
rotation is applied, but without any animation, just a flick between both position
Two wild guesses:
Is the rotation value part of the DiffUtil.Callback? (the value or something to indicate that the item changed).
Did you try for the sake of Android to replace view.animate().rotation(180f) with something more silly like:
view.post( ... )
This would perform your animation inside the next rendering frame; I have a suspicion the animation is getting buried there and the final state is shown (the thing rotated already).
I have a ViewPager (using a FragmentStatePagerAdapter to page fragments). The user can use left/right swiping to page from one fragment to the next; this works fine. I've also added < and > buttons for paging. The onClick listeners for these are implemented using:
mViewPager.arrowScroll(View.FOCUS_LEFT);
and
mViewPager.arrowScroll(View.FOCUS_RIGHT);
This works most of the time: when the user taps < or >, the view scrolls left or right as expected.
But on the first tap of the > button, nothing happens. I do hear a "click" confirming that the tap occurred. And I have log statements showing that the onClick listener was called, and mViewPager.arrowScroll(View.FOCUS_RIGHT) was called. I even check the result return by arrowScroll(); it's true! Yet no paging happens.
After that, all tapping on the < and > buttons works fine.
Why would the first call to arrowScroll(View.FOCUS_RIGHT) have no effect, and how can I fix it?
I guess I can try calling it twice the first time, but since I don't know why the documented behavior isn't happening, I don't know whether that approach will cause a double paging on some phones or Android versions.
Update: some logging
// Before any button taps.
QuestionAdapter: getItem(0)
QuestionAdapter: instantiateItem: position 0
QuestionAdapter: getItem(1)
QuestionAdapter: instantiateItem: position 1
QuestionAdapter: setPrimaryItem: position 0
QuestionAdapter: setPrimaryItem: position 0
// The screen is displaying page 0.
// Now I tap the > button:
QuestionAdapter: setPrimaryItem: position 0
// The screen is still displaying page 0.
// Now I tap the > button again:
QuestionAdapter: getItem(2)
QuestionAdapter: instantiateItem: position 2
QuestionAdapter: setPrimaryItem: position 1
QuestionAdapter: setPrimaryItem: position 1
QuestionAdapter: setPrimaryItem: position 1
// Now the screen displays page 1.
OK, I think I'm starting to figure out the problem. When I trace ViewPager.arrowScroll(FOCUS_RIGHT) (source code here) it looks like what it's doing is trying first to move the focus to the right (hmm, maybe that explains why the argument says FOCUS_RIGHT!).
So depending on what currently has focus, and whether there's a nextFocus view that's to the right of it, it will just move focus there, consider its job done, and return true to signal success.
Only if there's not a nextFocus view to the right will it actually pageRight() as I want it to do.
In my case, when I first press >, currentFocus = this.findFocus() returns null. Then it calls nextFocused = FocusFinder.findNextFocus() and comes up with a ListView that's on the currently displayed page. Since currentFocus is null and nextFocus is not (among other conditions), arrowScroll() is satisfied with setting focus to the ListView.
The second time I tap >, currentFocus = this.findFocus() returns the ListView, and nextFocused = FocusFinder.findNextFocus() yields null. Because nextFocused is null, it doesn't try to nextFocused.requestFocus() but instead calls pageRight(), which is what I wanted in the first place.
That being the case, what is the solution that fits the design of the ViewPager? It sounds like arrowScroll() is not intended for just paging left/right, like a left/right button would be expected to do. Instead it's meant to do what a keyboard arrow key should do; hence the name.
So then what method should be used to just page left/right, without regard for what currently has or can get focus?
I could try to work around arrowScroll's behavior by setting focus to the right view before calling arrowScroll, but that seems like a kludge. There are the pageRight and pageLeft methods, which look like they do exactly what I need, but they're not public, nor documented!
Well, pageRight and pageLeft call setCurrentItem(mCurItem + or - 1) to do their work, a method that is public and documented. So I guess I can just copy the code for pageRight and pageLeft into my own code.
This API design seems strange to me, given how common it is to have left/right buttons on a pager screen that are expected to page left/right regardless of focus. It's also frustrating that the documentation for arrowScroll(), and for paging the ViewPager left/right, is so vague.
But in any case I think I've found a decent solution.
I have xxx of type ImageView. If I click it, the event handler animate will be triggered to animate it.
public void animate(View view) {
ImageView xxx= findViewById(R.id.xxx);
xxx.animate().rotation(7200f).setDuration(3000);
}
It works only for the first click but for the subsequent clicks the animation does not work.
Question
How to fix this issue?
Keep in mind that rotation sets the rotation value to the provided float.
You are probably looking for rotationBy if you want to always rotate by that value
I have a simple horizontal recyclerview. As the user scrolls, the current item looses focus has background set to transparent and the new focused item is to have its background color changed to green. I have a simple method that takes in the focused position, changes it's color and sets the reset of the items to transparent.
public void resetRecycleColor(int rowindex){
for(int i=0; i < mRecyclerView.getAdapter().getItemCount(); i++){
if(i== this.rowindex){
Log.v("SCROLLS ", "COLOR GREEN "+ i);
mRecyclerView.getLayoutManager().findViewByPosition(i).setBackgroundColor(Color.GREEN);
}else{
Log.v("SCROLLS ", "COLOR TRANSPARENT "+i );
mRecyclerView.getLayoutManager().findViewByPosition(i).setBackgroundResource(R.color.gridBackgroundBlack);
}
}
}
Am I attempting to change the item background correctly? Something is wrong because it crashes on the line where the color is set. Comment out those lines and scrolling works fine. Can somebody point out why the crash may occur?
Thanks
You don't seem to understand the ViewHolder pattern.
You only have enough views (plus about 2) to fill RecyclerView. For example if you have 2000 items but they are represented in list that shows 3 items each, you only have 5 ViewHolders created. That way when scrolling views that exit the screen, reenter from the other side but displayed values are changed (in OnBindViewHolder).
Crash is occurring in findViewByPosition(i) since you're trying to get views for entire list but you only have views for a few of them, so this returns null.
I don't understand your use case entirely, but if you're repeatedly calling your method during onScroll then it has really bad performance, cleaner solution would be modifying your adapters onBindViewHolder to properly modify focused view and calling notifyItemChanged() to trigger rebinds on new/old focused view.
I've got a GridView with a custom Adapter, displaying thumbnails of pictures to the user. I can select and deselect pictures (I use setAlpha(0.25) on the views to notify the user of the change), and that's all well and fine.
Now what I want to do next, is have a button on top of the gridview that clears the whole selection, i.e. call setAlpha(1.0) on all views that were changed. So far I can reset my elements in the adapter, and I can setAlpha to 1, but the view doesn't update unless I scroll it out of the display, and then get back to it, or notify the adapter of changes, which redraws all my views, which does not look too pretty if only one element was selected.
I already dynamically set and reset individual elements through the GridView's onClickListener, but can't do this for more. I even tried calling performClick on all selected items through my button, but again it only displays the changes after the views have been out of the screen and are shown again.
Here's how I simulate the clicks:
for (int i = 0; i < mAdapter.getCount(); i++) {
PictureForSelection tempPic = (PictureForSelection) mAdapter.getItem(i);
if (tempPic.isPicSelected()) {
//tempPic.setIfSelected(false);
gridview.performItemClick(mAdapter.getView(i, null, null), i, i);
}
}
EDIT:
Conclusion - don't simulate clicks like this :)
So now I skip click simulation and use this:
gridview.getChildAt(i).setAlpha((float) 1.0);
In general it does exactly what I wanted it to do, except for one case:
If I have for example 3 pictures selected, one is off screen, one is partly shown, and one is fully shown, the ones shown (even partially) don't update their views.
If all the selected pictures are displayed it works flawlessly, but if some are out of the display, the rest do not update until the adapter's getView() gets called by Android. Still, thanks #mmlooloo for getting me so far.
If anyone knows a way around this, please do share :)
After setting your alpha you can call imageview.invalidate(); this causes your imageview to redraw itself.
if notifyDataSetChanged() is not working you can try like this.
i'm not sure whether this works,
1.onclick of the button you set the gridView.setAdapter(null);
2. Then set a new CustomAdapter object to gridview with default values.