I'm trying to find some kind of elegant solution for fading in/out a TextView that is part of an item in a ListView.
To give you some context, the listview shows a list of players in a basketball game. The user taps on a name and is provided with a dialog to log an event, for example a shot or a foul for that player. Once the dialog is dismissed the user is brought back to the listview and it is here that I'd like to provide some feedback about the event that has just been logged.
The way I'd like to do it is to have a small string appear for about 5 seconds in the view of the item (player) that has just been tapped on. The small string would display something like "3rd foul" or "4 turnovers".
A naive implementation is straightforward. Change the text of the view to the required string and then start an animation that fades in the view, keeps it there for a while and then fades it out. Problems arise however when a second event for the same player is logged shortly after the first. Ideally the first feedback string should be allowed to stay for the allotted 5 seconds and the second string should fade in/out in the next 5 seconds.
This queueing of animation and text changes on a per-player bases I'm not quite sure how to implement. Furthermore, I'm concerned by the interaction between the animations and the Activity's life cycle. What happens (or should happen) to the queued animations when the activity is sent to the background, stopped or even removed from memory? Or when an item is removed from the ArrayAdapter behind the listview?
Thoughts?
Manu
Don't worry about the lifecycle of the activity. There will be no adverse effects. However if the activity goes into the background during the animation, the animation will take place and you will not see it.
As for haveing one animation wait for the next, simply do this:
// here we will keep track of any listView and when the last animation took place.
// The keys will be your listView identifiers. Here I assumed an integer, but use whatever is a good ID for your listView
private HashMap<Integer, Long> listViewLastAnimations;
// the length of the animation in milliseconds
private static long ANIMATION_LENGTH_MS = 5000;
// put this code where you would start your animation
// get when the last event animation occurred
Long lastAnimation = listViewLastAnimations.get(YOUR_LIST_ITEM_IDENTIFIER);
Date new = new Date();
if (lastAnimation == null ||
new.currentTimeMillis () - lastAnimation > ANIMATION_LENGTH_MS ){
listViewLastAnimations.put(YOUR_LIST_ITEM_IDENTIFIER, new.currentTimeMillis ());
// perform animation as normal
}else{
// set a delay to your animation with
long delay = ANIMATION_LENGTH_MS - (new.currentTimeMillis () - lastAnimation);
listViewLastAnimations.put(YOUR_LIST_ITEM_IDENTIFIER, new.currentTimeMillis () + delay);
setStartOffset(delay) ;
}
Related
I'm developing android messenger application and using a RecyclerView for showing messages. I have a button that loads all messages from server and scroll down to the last one by clicked on it. However, after clicking on this button I see only half of the last message's content. In addition, when I scroll a little up and click on this button for the second time I don't have a problem and see all content of last message.
Code of setuping the button.
private fun setupToLastMessagesButton() {
binding.toLastMessagesButton.setOnClickListener {
// Show progress bar while messages is loading
binding.mainProgressBar.visibility = View.VISIBLE
// Tell service's thread to load messages by using handler and post mechanism
getServiceHandler().post {
val result = getService().getLastMessages()
// Tell main thread to update recycler view and scroll down
mainHandler.post {
// Finish showing progress bar
binding.mainProgressBar.visibility = View.GONE
// Notify adapter
getAdapter().notifyItemRangeInserted(result.first, result.second)
// Scroll to last element
binding.messagesView.scrollToPosition(getAdapter().itemCount - 1)
}
}
}
}
RecyclerView .xml code
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/messagesView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="#+id/recyclerViewProgressBar"
app:layout_constraintTop_toTopOf="parent" />
Screenshot shows my problem. You can see that last element have incorrect layout and you can't see a text of message.
I guess that RecyclerView doesn't have time to complete relayouting before calling 'binding.messagesView.scrollToPosition(getAdapter().itemCount - 1)'. I tried to call this function after some delay, but nothing changed. Is there a mechanism to wait a moment when relayoting is completed?
Screenshot shows behaviour I'm expecting.
you should call scrollToPosition in yet another post, give a moment for RecyclerView for obtaining data and measure/draw (in memory, first frame) new item.
binding.messagesView.post {
binding.messagesView.scrollToPosition(getAdapter().itemCount - 1)
}
currently it is scrolling to half of this new item probably because it isn't drawn yet and RecyclerView "thinks" that this item is smaller (so scrolling a bit to small amount of pixels, not to the end). I bet your "clear" item/layout (not filled with msg data) doesn't contain this gray area with numbers, so RecyclerView scrolls only to always visible username-row
I have an array of images like this:
int[] fruitList = new int[] {
R.drawable.apple,
R.drawable.orange,
R.drawable.bannana
};
and an ImageView. For every fruit i want to a) populate the ImageView b) wait for click to open a Dialog (Choose fruit name and do something ...) and repeat for next fruit.
I thought of a for loop but doesn't make sense. I need to wait for the click before going to the next fruit.
Any ideas ??
Hold an index for your position along the fruitList
Set an onClickListener on the Image View.
Create a Dialog in the onClickListener
Subscribe any clicks in the dialog, and in that subscription change the fruit by incrementing your index (maybe % fruitList.size?)
In the onClickListener launch the dialog
The key difference between app development and smaller programs is that apps are all event based. A for loop isn't suited for this kind of task.
You can use Carousel view for it. You can use following repositories for it. Choose what you want
https://github.com/sayyam/carouselview
https://github.com/Azoft/CarouselLayoutManager
https://android-arsenal.com/tag/154
https://medium.com/#lobothijau/create-carousel-easily-in-android-app-with-carouselview-6cbf5ef500a9
I need to write an android application like a book. I have approximately 100 images and i need to show them with back, forward and another buttons.
I tried to create an xml-layout for each image and made images to layout's background.
While running application, if i press buttons fast, program crashes during switching xml-layout.. If i decrease image sizes my problem decreases also. Unfortunately, i need another solution in order to solve it because i cannot use smaller image-size but i have crash problem still.
Have one layout, with an ImageView in it. Then keep changing the source image for the image view whenever you need to cycle to the next or previous image.
Part of the problem is that clicking a UI button returns immediately / queues clicks, even though the action associated with that click has not yet completed. For reasons beyond the scope of this response, its worth noting that simply deactivating the button while "doing work" is ineffective. There are a couple solutions to this kind of problem: One is to use a boolean flag that gets set only after the underlying "work" has completed. Then within the button action handler, you ignore button clicks that occur before the flag is reset:
/**
* Button presses are ignored unless idle.
*/
private void onMyButtonClicked() {
if(idle) {
doWork();
}
}
/**
* Does some work and then restores idle state when finished.
*/
private void doWork() {
idle = false;
// maybe you spin off a worker thread or something else.
// the important thing is that either in that thread's run() or maybe just in the body of
// this doWork() method, you:
idle = true;
}
Another generic option is to filter using time; ie. you set a limit where the max frequency of button presses is 1hz:
/**
* Determines whether or not a button press should be acted upon. Note that this method
* can be used within any interactive widget's onAction method, not just buttons. This kind of
* filtering is necessary due to the way that Android caches button clicks before processing them.
* See http://code.google.com/p/android/issues/detail?id=20073
* #param timestamp timestamp of the button press in question
* #return True if the timing of this button press falls within the specified threshold
*/
public static synchronized boolean validateButtonPress(long timestamp) {
long delta = timestamp - lastButtonPress;
lastButtonPress = timestamp;
return delta > BUTTON_PRESS_THRESHOLD_MS;
}
Then you'd do something like this:
private void onMyButtonClicked() {
if(validateButtonPress(System.currentTimeMillis())) {
doWork();
}
}
This last solution is admittedly non deterministic, but if you consider that users almost never intentionally click button more than 1-2 times a second on a mobile device, its not so bad.
My application is processing data (which involves lot's of calculation, creation of arrays, clearing of those arrays) is it possible to resume from the place it left the calculation when the application resumes ?
While the calculation is going on I am showing a progress bar which cannot be cancelled using the back button, but in-case the user clicks the home button I do not want my calculation to be lost but instant stay exactly at the point till the application is run again and display the final results.
Thanks
====== Edit ======
I call ProcessingAsync().execute() in the onCreate method
The content of doInBackGround of my ProcesingAsync class is given below: (Using spinner and not horizontal progress bar to show the activity is going on)
#Override
protected String doInBackground(String... aurl) {
recordedDataFile = new File(Environment.getExternalStorageDirectory().getPath(), "Machine_Health_Monitoring/Temporary_Files/Fault" + faultNumber + FORMAT_WAV);
runThisCalculationMethod(2, recordedDataFile);
// showWaitingMessage becomes false after the runThisCalculationMethod() is completed
while(showWaitingMessage){
}
return null;
}
You can create a marker (that shows in which part of the calculations you're at) and a partial-result and save it to a file. When you try resuming you'll first look in the file (if it exists) and continue from the last saved point. After you've finished all your calculations and displayed the results - make sure to delete the file.
I'm making a quiz-like game, where user answers each question before they are allowed to go to the next one.
Characteristics of my app:
Each session will have
around 10-30 screens.
In general, the screens are heterogenious in layout
structures, but they can be
classified into 5-6 types.
The expected time that user interacts with each screen is 10-30
seconds
Once user goes to the next screen,
the previous one is not needed
anymore (he never goes back)
I want to have a nice sliding
transition animation when going from
one screen to the next
Implementations I'm considering:
Start a new Activity for each
screen in the 'forwarding' style,
i.e. start the next screen then
finish the current one.
Load all the views before hand and
use ViewAnimator
It looks like none of my current solution is good. Can you help me on a solution that is good in terms of memory consumption, battery consumption, and responsiveness?
Thank you very much.
OK below is what I did. It turned out I manually set the animation
onCreate() {
mAnimation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide_in_right);
mViewPool = new View[] { /* A few views for re-using, each of different type */ };
}
proceed() {
nextView = getView(type);
mFrame.removeAllChilds();
mFrame.addView(nextView);
nextView.startAnimation(mAnimation);
}
getView(int type) {
View view = mViewPool[type];
// reset some subviews if neccessary
return view;
}
Where mFrame is whatever ViewGroup you think is appropriate, not neccessarily ViewAnimator. Mine happens to be ScrollView.
If you see any potential problem with this approach, please let me know. Many thanks.