Android Karaoke-like : Updating GUI from MediaPlayer.currentPosition() - android

I'm making some kind of "karaoke" android apps
So, basically, I have some TextViews and I want to change the text color programmatically while playing audio.
TextViews have the "begin_time" and "end_time" attributes which relates to the position in the audio file
Playing audio, and gathering informations about that is easy with MediaController
So, now I'm trying to change the color dynamically ... I tried with a separate thread and many other ways, but I always stuck with the same issue
" android.view.ViewRoot$CalledFromWrongThreadException: Only the
original thread that created a view hierarchy can touch its views. "
I tried to learn more about that, and what I learned is that everything related with the UI has to be done in the UI Thread.
Most of people use handlers to perform GUI update in background thread. But I didn't made this damn thing worked out ... So any suggestion about handlers and how they work is welcome.
Anyways, why not using AsyncTask ?
Quote from the "Processes and Threads dev' guide"
" Perhaps the best solution, though, is to extend the AsyncTask class,
which simplifies the execution of worker thread tasks that need to
interact with the UI. "
Actually, I'm using this AsyncTask, but it doesn't "simplifies" threading
Or Maybe they're is an other solution for GUI update from time event ?
Well, in fact, i don't want to make "just a karaoke" Textview have to be clickable, so when the user click on a word, the audio goes to that position in audio, and textview color have to change dynamically
So, I thought about using Animation for smooth color changing. Or maybe TransitionDrawable, but it use layers and canvas.
I also find out this, with ForegroundColorSpan:
Spannable WordtoSpan = new SpannableString("I know just how to whisper");
WordtoSpan.setSpan(new ForegroundColorSpan(Color.BLUE), 1, 13, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView tv = new TextView(context).setText(WordtoSpan);
But not sure it will be dynamic enough due to calculation of the end of the ForegroundColorSpan.
So considering using only TextView, what is the best way to achieve dynamic textview color changing (with those solutions, or any other)?
Second edit
Well, i'm almost done with this project ...
I am now able to color word dynamically over time, but the problem is that it's not synchronized with audio.
i.e :
let's take a sentence like "hello, my name is john and i'm late for my job"
If audio is playing [i'm] the colored text is [job]
So, it's just a synchronization problem, but i'm facing it for several weeks.
The text is organized into a linked list
elements of the linked list are Object (because i'm having both Word and Ponctuation class) but both have a begin and end position, corresponding to their relative position in audio in milliseconds.
so ... I have something like that :
Hello : begin = 0, end = 1000
my : begin = 1200, end = 1400
and so on ...
In the main activity now, I have a Runnable that deal with text coloration
And I also have 3 Object, previous_element, current_element, and next_element
At the begining of the runnable, the color of the previous_element is set back to White, so only the current word is colored.
Then, the current_element's color is set to Yellow, or whatever ...
Then, I check if next_element.getBegin() >= current position in audio
... If it is, that mean the current word is now over, so I change the current_element to next_element, and start over the process using Handler.post(MyRunnableThatDoesColoration)
So I'm not understanding where it's going wrong
If anyone one have a clue ?

" android.view.ViewRoot$CalledFromWrongThreadException: Only the
original thread that created a view hierarchy can touch its views. "
This can be achieved by using runOnUIThread:
runOnUiThread(new Runnable() {
public void run() {
//Your UI Changing code HERE!
}
});
If you see the karaoke as basic gui (textViews), I think it won't be dynamic enough. You should use Android Canvas or a gameEngine (Andengine..).

Related

android kotlin click listener loses its "click methods" after some time

Hi I'm implementing click listeners in the following way but after some time the methods and variables inside the listener's closure get the wrong values or something. Let me explain the implementation of the listener a little better a for loop creates the listener for a set of image views then later in the program the for loop is called a second time and it resets the listener methods and variables to different values. Everything works great for about 30 minutes but then for some reason, the listener's methods and variables start having the wrong values. Has anybody ever heard of this behavior or can tell me where I've gone wrong with the code? Keep in mind that the listener I'm about to paste here is just a small piece of a 1014 line class. I'm hoping somebody can spot How I'm implementing the listener wrongly and can give me some advice on how to "reset" the listener so that it's variables and values stay over time. Hopefully you can read the code without putting it in an editor but feel free to copy it for readability's sake Here is the code for the image view listener with comments.
//image views are held in an array
//set an image view in its imageview container
imgArr0[slotId1].invalidate()
imgArr0[slotId1].setImageDrawable(null)
//drw is not defined in this example
imgArr0[slotId1].setImageDrawable(drw)
/*if video or image id is set to image then set a listener for the image
*/
/*slotId1 is not defined in this example but it is simply a counter to iterate over the ImageView array
*/
if (videoOrImageId0[slotId1] == "image") {
//null any listeners that might be attached to the image view
imgArr0[slotId1].setOnClickListener(null)
//set or reset the listener
imgArr0[slotId1].setOnClickListener() {
`enter code here`//if the current config is portrait then set a new image image
if (currentConfig0 == "portrait0") {
act0.lrgImage0.invalidate()
act0.lrgImage0.setImageDrawable(null)
/*drw is not defined in this example but works fine in the production script
*/
act0.lrgImage0.setImageDrawable(drw)
}
--calmchess
ccc tv application with problem.
(https://i.stack.imgur.com/PjdbN.jpg)![enter image description here](https://i.stack.imgur.com/FaMnc.
I was able to partially solve this question by destroying all the image views and their associated click listeners then rebuilding those... However I don't consider this issue completely solved so if anybody can provide a better solution I'd love to hear it because rebuilding the images every few minutes has to be using a lot of unnecessary hardware resources.
--calmchess

TextView.setMaxLines not working?

In my app I have a screen where I display some text and then a photo. The text is variable in length (sometimes none at all, sometimes a lot), so I wanted to have it set up so the text never takes up more than a few lines (but can be scrolled) leaving enough room for the image below.
My view component for this part is created programatically, and I've adjusted the code to have the following (currently in my text-setting method, but the same thing happens if it's in the initial view-create code)
public void SetDescription(String description)
{
mTxtDescription.setText(Html.fromHtml(description));
mTxtDescription.setClickable(true);
mTxtDescription.setMaxLines(5);
mTxtDescription.setLines(5); //this makes no difference either!
mTxtDescription.setSingleLine(false);
mTxtDescription.setScrollbarFadingEnabled(true);
mTxtDescription.setScrollBarStyle(VERTICAL);
mTxtDescription.setMovementMethod(ScrollingMovementMethod.getInstance());
mTxtDescription.invalidate(); //adding this made no difference...
}
However it doesn't work- long text still fills the whole screen and the image has vanished due to being pushed down to a height of 0. How can I get the text to never be more than 5 lines?
Try removing the call to setSingleLine. And use setInputType(InputType.TYPE_TEXT_FLAG_MULTI_LINE). It'd also put this call before the setMaxLines and setLines call to be sure.
Note: setLines overrides the settings of setMaxLines and setMinLines.
The TextView has many issues surrounding the various calls to how it should display multiple, ellipses, etc.
The setSingleLine(false) seemes to reset the setMaxLines command. Try to move the setSingleLine command before the setText. That worked for me.
The below code is working fine for me
txt = (TextView)findViewById(R.id.textview);
txt.setMaxLines(5);
txt.setMovementMethod(new ScrollingMovementMethod());
txt.setScrollContainer(true);
txt.setText("Example Text");
txt.setTextColor(Color.WHITE);
txt.setScrollbarFadingEnabled(true);
in xml inside textview
android:scrollbars="vertical"

Multiple TextViews update very slowly

I have a Service that sends an Intent to my Activity every 0.1 seconds. I use it to update a custom implementation of a Chronometer. Here everything goes right. The problem comes when I want to update 14 TextView I have in a TableView inside a Fragment in my Activity. Here the app is very slow.
The method in my Activity where it receives the Intent from the Service:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long milis = intent.getLongExtra("milis",0);
if( mFragment != null)
mFragment.Update(milis);
}
};
The code inside the Fragment where I update the TextViews:
public void actualizarTiempoJuego(long milis){
// Se recuperan los tiempos acumulados y se aumenta la cantidad pasada como parĂ¡metro
for(int i=0;i<7;++i) {
long mCurrentMilis1 = mVectorMilis1.get(i);
long mCurrentMilis2 = mVectorMilis2.get(i);
TextView1 t1 = mListaTitularLayoutLocal.get(i);
TextView1 t2 = mListaTitularLayoutVisitante.get(i);
t1.setText(String.value(milis + mCurrentMilis1));
t2.setText(String.value(milis + mCurrentMilis2));
}
}
Am I doing anything wrong, or is it just that I'm trying to do something very complex in terms of efficiency?
#Sherif brings up a good point about hidden alpha values that bog down your application a lot.
Depending on your platform you may also want to check
<application android:hardwareAccelerated="true"... />
Another thing you can look into that may help performance is not firing off all those Intents. Once you start firing intents you are getting the system involved and depending on how they are getting resolved it may take some extra time.
For this issue I like to use Handlers. They are more light weight than intent.
You may also want to look at AsyncTask. This is basically like a thread, but also gives hooks that run on the UI Thread so you can perform both perform a background operation and update the UI without have to post runnables.
EDIT: Lastly, you can always run your layouts through the layoutopt tool. I was personally told by Romain Guy himself that if your drawing too slow, than you need to draw less. Just check out a screenshot (from a less than ideal view tree, but well within the max) from the profiling tool. You can see how much of the resources view drawing takes up. It's very important to keep this as lean as possible if you want your app to be responsive.
EDIT: It is no longer called layoutopt, it's called lint. Check your ~/android-sdk/tools/
I have once faced a situation where a fragment was really slow.
I am just predicting that your fragment has some kind of alpha and it is drawn on a 'heavy' activity.
The conclusion is that each time you are setting the text of a textview your whole view hierarchy is being invalidated.
It seems that fragments have this flaw. Anyway, use some layout instead of the fragment and check if it remains 'slow'.
ADDITION: A wrap_content textview will cause much more delay after a setText than a fill_parent textview.
You're likely running into slowdowns due to layout management with TableLayout and TextView. Every time you update text in one of those, a large amount of view measuring has to take place in order to put the characters in the right place on the screen. You should really just profile the app yourself using Traceview to find out. More information at: http://developer.android.com/tools/debugging/debugging-tracing.html
I've had the exact same issue you're seeing with the same type of layout (Fragment > TableLayout > Multiple TextViews). One way to test if your TableLayout/TextView setup is to blame is simply replace all that with a single TextView. That will probably run pretty well. Then put your 14 views into a FrameLayout or RelativeLayout. Even if they all overlap, you should still get decent performance, because it's the complexity of the TableLayout view measurements that's really causing slowdown.
As someone said you can use HardwareAccelerated but this is not a great solution, you will waste ram and cpu if you can't solve it in a different way. A solution probably more safety is to reduce the number of TextView. Try to reduce 14 to 7 and it will go twice faster. Usually is hard to do it but if you put the objects in a strategy position a pair of TextView one above other can be together if you make a TextView with two lines. And don't forget that findViewById is so expensive, if you will use a view object often find it one time and hold its reference.
Benchmarks are always useful for determining where slowness actually comes from, but I feel pretty confident suggesting that sending an Intent is probably much slower than updating 14 TextViews. Sending 10 Intents per second is a sign that you're Doing It Wrong (TM). This is just isn't what they're for.
Am I doing anything wrong, or is it just that I'm trying to do something very complex in terms of efficiency?
Updating 14 TextViews per second isn't inherently complex; you should be able to easily achieve this with a more appropriate application design. ASyncTask or Handler come to mind as possible tools, but it's hard to know what's best without knowing more about exactly what you're trying to do.
You can try to declare vars outside the loop :
public void actualizarTiempoJuego(long milis){
// Se recuperan los tiempos acumulados y se
// aumenta la cantidad pasada como parĂ¡metro
long mCurrentMilis1;
long mCurrentMilis2;
TextView1 t1;
TextView1 t2;
for(int i=0;i<7;++i) {
mCurrentMilis1 = mVectorMilis1.get(i);
mCurrentMilis2 = mVectorMilis2.get(i);
t1 = mListaTitularLayoutLocal.get(i);
t2 = mListaTitularLayoutVisitante.get(i);
t1.setText(String.value(milis + mCurrentMilis1));
t2.setText(String.value(milis + mCurrentMilis2));
}
}
And to setText() with mixed type, you can try setText("" + milis + mCurrentMilis2);

Suspend UI Layouts in Android

I have a bunch of code in a routine that looks a bit like this:
a.setContentView(R.layout.myLayout);
textview t1 = (TextView) a.findViewById(R.id.mylayout_t1);
t1.setText("Hello")
t1.setTypeface(font);
t1.setTextColor(colour);
t1.setTextSize(fontSize);
textview t2 = (TextView) a.findViewById(R.id.mylayout_t2);
t2.setText("Hello Again")
t2.setTypeface(font);
t2.setTextColor(colour);
t2.setTextSize(fontSize);
The problem I'm having is that before when the routine is called, the layout is done with all the fonts at the default font/size/colour and then they quickly change to the specified values, which is not very pleasant on the eye.
Is there some kind of command I can add to the beginning of the routine to suspend any layout, and then another command to resume at the end of the routine?
There are two ways:
1) Put your all code (you mentioned above) in onCreate() method and at last call t1.setVisible(true);
2) Put your code in the method in which you are creating your UI (like initUI() or something like that) and call this method before setting visibility to true.
Have you considered using XML to set the text style instead of doing it programmaticly. See this Android Dve Guide page for more on this topic.
Another (bad?) way might be to use XML to set the views visibility to false and when you have made your style changes, call t1.setVisibility(true). Haven't tried this one, so it might produce a similar, unwanted result.

Simple TextView.setText causes 40% CPU Usage

Running my application causes ~40% CPU usage on my Phone:
final String position = String.format("%02d:%02d:%02d", time.getHours(), time.getMinutes(),
time.getSeconds());
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
c.mTxtPosition.setText(position);
...
By commenting out the setText method the CPU Usage drops to the expected level of ~4%. The method is invoked every second and does refresh ImageViews, CustomViews ... without causing the same load excess.
Besides the CPU Usage dalvik constantly reports garbage collecting of about 10-1000 objects just by calling setText().
Creating a tracefile like this:
Debug.startMethodTracing("setText");
c.mTxtPosition.setText(position);
Debug.stopMethodTracing();
traceview lists the following methods as Top 5 by their respective exclusive CPU%:
ViewParent.invalidateChildInParent(16%)
View.requestLayout(11%)
ViewGroup.invalidateChild(9%)
TextView.setText(7%)
toplevel(6%)
Has anybody an explanation for this?
I noticed this myself a while ago, I think the problem is that every time you call setText, the size of the textbox can change, thus requiring the entire screen to go through relayout (expensive).
I haven't tried this myself yet, but if your textbox is simple and can be made to be a relatively fixed size, maybe try to subclass TextView and create a view that does not resize itself on setText, but rather just draws whatever it can into the existing area? That would save a lot of time.
Perhaps theres already a flag to setText that can make it do this, but I'm not aware of it, though I haven't searched closely.
In my case, I update a TextView from touch event, which cause a lot of updating The solution was to change the TextView layout_width & layout_height to fixed sized.
some possible improvements :
try using a handler which updates the textview every 0.5 seconds instead of a thread that does it.
make the runnable a final constant object instead of craeting a new one every second.
consider checking that the time has changed (newTimeInMs-LastPublishedTimeInMs>=1000) before telling the textview to update itself.
instead of String.format , try using StringBuilder . however , you won't enjoy the locale solution that the String.format gives (for example , for arabic digits) .
In my case it was this property of TextView:
android:ellipsize="marquee"
Removing it speeded up setting text.
If you look at the source code of setText method you can see that it does a lot of heavy lifting - there is measuring, drawing and object allocations, all of which run on the main thread.
You can use the new PrecomputedText API in order to do all of this on the background thread and make setText faster.
You can use the following working example using kotlin & coroutines
private fun TextView.setTextAsync(text: String) {
val textView = this
lifecycleScope.launch {
val params = TextViewCompat.getTextMetricsParams(textView)
val precomputedText = withContext(Dispatchers.Default) {
PrecomputedTextCompat.create(text, params)
}
TextViewCompat.setPrecomputedText(textView, precomputedText)
}
}
For more details you can read an article about it on my blog
https://androidexplained.github.io/android/ui/2020/10/21/improving-textview-settext-performance.html

Categories

Resources