Im trying to create a generator, when i click the start button, the textview gives me a a random number.
This works great,
but i want to show the numbers still i click stop
so it must give me every few secons a new random number from my mutable List.
How could I do this?
My first opinion was, recursive function.
private fun run() {
var x = 0
var randomListe = mutableListOf<Int>()
randomListe.add(Random.nextInt())
x++
for (element in randomListe) {
var x = 0
val zahlInListe = randomListe[x]
// Thread.sleep(1_000)
// I tried while here
textView.text = ("${randomListe[x]} \n das ist die Random Zahl moruk\n")
}
}
You can launch a coroutine block in lifecycleScope for this and you can also remove redundant x for keeping the current index to show the last value, you can use the last method of ArrayList to get the last value in the list
Make changes to your run method and return a Job object from it so you can cancel it later when the user taps on the stop button.
private fun run(): Job {
return lifecycleScope.launch {
while(true){
delay(1000)
var randomListe = mutableListOf<Int>()
randomListe.add(Random.nextInt())
textView.text = ("${randomListe.last()} \n das ist die Random Zahl moruk\n")
}
}
}
Now keep the return job value in a variable, on calling the run method
private var job: Job? = null
job = run()
Call cancel on job when the user taps on the stop button
btnStop.setOnClickListener{
job?.cancel()
}
Related
I’ve run into a performance obstacle and I’m uncertain of the cause, all of this is running under VS2022 & Net6.0. As this is my 1st time using this combination of a modal windows form, and progress bar, with the work running on a background thread and two Progress objects updating the UI, the progress bar, and a text label, I don’t know where to attack the problem. Prior to placing the workload on a background thread, everything was snappy, searching a thousand files with about 600 lines of text in each, in about a minute. Naturally, the windows form was frozen during this, which is why the workload was placed on a background thread.
After doing so, the workload will be 25-50% complete before the UI starts displaying the values from the Progress objects, and overall, the entire process now takes 10x as long to complete. Progress objects aren’t skipping over any values sent to them, the UI thread just seems slow in getting the information. Likewise, if I try to drag the modal form to a new spot on the desktop it’s unresponsive for 20—30 seconds before it finally moves. One more thing, I can step through the code on the background thread and see it calling the Progress updaters, but the UI thread is just very slow in responding to them.
I could use some suggestions on how to uncover the problem or if clearly evident, point out where the likely problem could be. Here are the essential controls and methods used.
public class SearchProgressForm : Form
{
private System.Windows.Forms.Button btnSearch = new Button();
private System.Windows.Forms.TextBox txtTextSearch = new TextBox();
private System.Windows.Forms.Label lblSearchFile = new Label();
private System.Windows.Forms.ProgressBar SearchProgressBar = new ProgressBar();
public event LogSearchEventHandler SearchSucceededEvent;
protected void OnSearchSucceeded(LogSearchEventArguments p_eventArguments)
{
LogSearchEventHandler handler = SearchSucceededEvent;
if (handler != null)
{
handler(this, p_eventArguments);
}
}
private void InitializeComponent()
{
this.btnSearch.Name = "btnSearch";
this.btnSearch.Text = "Search";
this.btnSearch.Click += new System.EventHandler(this.btnSearch_Click);
this.lblSearchFile.Text = "Searching File: ";
this.txtTextSearch.Text = "search string";
}
public SearchProgressForm() { }
private void btnSearch_Click(object sender, EventArgs e)
{
this.SearchByText(this.txtTextSearch.Text);
}
private void SearchByText(string p_searchParameter)
{
// Setup a progress report for thr ProgressBar
var _progressBarUpdate = new Progress<int>(value =>
{
this.SearchProgressBar.Value = value;
this.SearchProgressBar.Refresh();
});
var _progressFileNameUpdate = new Progress<string>(value =>
{
this.lblSearchFile.Text = "Searching File For : " + value;
this.lblSearchFile.Refresh();
});
// Start search on a backgroud thread and report progress as it occurs
Task.Run(async () => await this.SearchByStringAsync(p_searchParameter, _progressBarUpdate, _progressFileNameUpdate));
}
private async Task SearchByStringAsync(string p_searchParameter, IProgress<int> p_progressBar, IProgress<string> p_progressFileName)
{
await Task.Delay(1);
TextFileReader textFileReader = new TextFileReader();
LogSearchEventArguments logSearchEventArguments = null;
long _sessionloopCount = 0;
long _totalTextLinesCount = this.GetTotalSearchCount(p_searchParameter, SearchType.TextString);
// Get file names from SQL table
var _logFiles = DataOperations.LogFileSortableList(null);
foreach (var log in _logFiles)
{
// Format a file name to be read from the file system
string _fileName = log.Directory + "\\" + log.FileName;
p_progressFileName.Report(log.FileName);
// If we've raised an event for this file, then stop iterating over remaning text
if (logSearchEventArguments != null)
{
logSearchEventArguments = null;
break;
}
// Read in file contents from file system
List<string> _fileContents = textFileReader.ReadAndReturnStringList(_fileName);
long _fileTotalRecordCount = _fileContents.Count;
long _fileRecordCount = 0;
foreach (var _line in _fileContents)
{
if (_line.ToUpper().Contains(p_searchParameter.ToUpper()))
{
// Raise an event so search parameter and file name can be captured in another form
logSearchEventArguments =
new LogSearchEventArguments
(
"TextSearch", p_searchParameter, SearchType.TextString, true, log,
new DateTime(
Convert.ToInt32("20" + log.FileName.Substring(14, 2)),
Convert.ToInt32(log.FileName.Substring(16, 2)),
Convert.ToInt32(log.FileName.Substring(18, 2)))
);
// We found a match, so no further searching is needed in this log file,
// and it's been flagged in the DB, so raise the event to save search parameter and file name
// then break out of this loop to get the next file to search in.
this.OnSearchSucceeded(logSearchEventArguments);
break;
}
// These calcs are based on actual searches performed
_fileRecordCount++;
_sessionloopCount++;
p_progressBar.Report(Convert.ToInt32((_sessionloopCount * 100) / _totalTextLinesCount));
}
// Because we exit a search as soon as the 1st match is made, need to resynch all counts
// and update the progress bar accordingly
if (_fileRecordCount < _fileTotalRecordCount)
{
long _countDifference = _fileTotalRecordCount - _fileRecordCount;
// Add count difference to sessionLoopCount and update progress bar
_sessionloopCount += _countDifference;
p_progressBar.Report(Convert.ToInt32((_sessionloopCount * 100) / _totalTextLinesCount));
}
}
//Search is complete set Progress to 100% and report before exiting
p_progressBar.Report(100);
// Close the modal SearchForm and exit
this.Close();
}
}
I solved this problem but I'm still not certain of what caused it. I eliminated the method "private void SearchByText(string p_searchParameter)" and moved the code there into the btnSearch_Click event handler so I could call my background worker "SearchByStringAsync" directly from the button click event handler.
I also updated the EFCore NuGet Packages, which were version Net6.0 to version 6.0.4, because of single line of code in my Async background method, "var _logFiles = DataOperations.LogFileSortableList(null)".
That call returned a Sortable BindingList, using BindingList <T>. Between the NuGet updates and a minor change on a custom comparer method in my BindingList <T> class, the windows modal form now updates the ProgressBar and Label text as expected, and the form now responds immediately to user interaction.
I have managed to get the sharedPreferences saving values. But i don't know how to make it reference the text i am clicking on. In the // Close Alert Window section when i click ok to change the text. Ok dismisses alert dialog, then suppose to add the new price to list in sharedPreferences.
In the putString() if i use putString("Price$it", input.text.toString()).applyit doesn't appear to do anything. However if i use "Price1" any text i change is saved and upon reopening the app Price1is changed to the new price. So i know the method is working. i just have no clue how to save the particular text i am editing. I hope this makes sense. Thanks for your time.
// Created Private Price List
val sharedPreferences = getSharedPreferences("priceList", Context.MODE_PRIVATE)
//Price
(1..912).forEach {
val id = resources.getIdentifier("Price$it", "id", packageName)
val tv = findViewById<TextView>(id)
tv.text = sharedPreferences.getString("Price$it","0.00")
}
(1..912).forEach {
val id = resources.getIdentifier("Price$it", "id", packageName)
val tv = findViewById<TextView>(id)
tv.setOnLongClickListener {
//Alert Window
val alertDialog = AlertDialog.Builder(this#MainActivity).create()
alertDialog.setTitle("NEW PRICE")
val input = EditText(this#MainActivity)
//Alert Submit on Enter
input.setOnKeyListener { v, keyCode, event ->
if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
// Input changes text
tv.text = input.text
when {
tv.text.startsWith("-") -> tv.setTextColor(Color.RED)
tv.text.startsWith("+") -> tv.setTextColor(Color.GREEN)
else -> {
tv.text = "_"
tv.setTextColor(Color.DKGRAY)
}
}
// Close Alert Window
alertDialog.dismiss()
// TODO Save Price Table //THIS PART vvv
sharedPreferences.edit().putString("Price1", input.text.toString()).apply()
}
false
}
val lp = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
input.layoutParams = lp
alertDialog.setView(input)
alertDialog.show()
return#setOnLongClickListener true
}
}
You are shadowing it. In your scope you are referencing the argument of tv.setOnLongClickListener. Specify the argument name so it's not shadowed by inner lambdas.
(1..912).forEach { index ->
...
sharedPreferences.edit().putString("Price$index", input.text.toString()).apply()
}
First of all, I'm new in Kotlin, so please be nice :).
It's also my first time posting on StackOverflow
I want to literally STOP the current thread that I created but nothing works.
I tried quit(), quitSafely(), interrupt() but nothing works.
I created a class (Data.kt), in which I create and initialize a Handler and HandlerThread as follows :
class Dispatch(private val label: String = "main") {
var handler: Handler? = null
var handlerThread: HandlerThread? = null
init {
if (label == "main") {
handlerThread = null
handler = Handler(Looper.getMainLooper())
} else {
handlerThread = HandlerThread(label)
handlerThread!!.start()
handler = Handler(handlerThread!!.looper)
}
}
fun async(runnable: Runnable) = handler!!.post(runnable)
fun async(block: () -> (Unit)) = handler!!.post(block)
fun asyncAfter(milliseconds: Long, function: () -> (Unit)) {
handler!!.postDelayed(function, milliseconds)
}
fun asyncAfter(milliseconds: Long, runnable: Runnable) {
handler!!.postDelayed(runnable, milliseconds)
}
companion object {
val main = Dispatch()
private val global = Dispatch("global")
//fun global() = global
}
}
And now, in my DataManager, I use these to do asynchronous things :
fun getSomething(forceNetwork: Boolean ) {
val queue1 = Dispatch("thread1") // Create a thread called "thread1"
queue1.async {
for (i in 0..2_000_000) {
print("Hello World")
// Do everything i want in the current thread
}
// And on the main thread I call my callback
Dispatch.main.async {
//callback?.invoke(.........)
}
}
}
Now, in my MainActivity, I made 2 buttons :
One for running the function getSomething()
The other one is used for switching to another Controller View :
val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
DataManager.getSomething(true)
}
val button2 = findViewById<Button>(R.id.button2)
button2.setOnClickListener {
val intent = Intent(this, Test::class.java) // Switch to my Test Controller
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
startActivity(intent)
finish()
}
Is there a way to stop the thread, because when I switch to my second View, print("Hello World") is still triggered, unfortunately.
Thanks for helping me guys I hope that you understand !
A thread needs to periodically check a (global) flag and when it becomes true then the thread will break out from the loop. Java threads cannot be safely stopped without its consent.
Refer to page 252 here http://www.rjspm.com/PDF/JavaTheCompleteReference.pdf that describes the true story behind the legend.
I think that a truly interruptible thread is only possible through the support of the operating system kernel. The actual true lock is held deep down by the CPU hardware microprocessor.
I wrote a method which changes backcolor of the rows before painting gridview in devexpress. It works fine but I realized that my code begins slowing down. Then I've found that the event firing continuously. It never stops. How can I handle this? Is there any way to stop firing event manually after gridview painted or should I try to solve this problem with an another event or another method???
Here is my event:
private void gvStep_CustomDrawCell(object sender, DevExpress.XtraGrid.Views.Base.RowCellCustomDrawEventArgs e)
{
try
{
DataRowView drw = (DataRowView)gvStep.GetRow(e.RowHandle);
byte actionTypeID = (byte)drw.Row["ActionType"];
//string colorCode = (new DivaDs()).GetBackColor(actionTypeID);
string colorCode = divaDs.GetBackColor(actionTypeID);
Color backColor = ColorTranslator.FromHtml(colorCode);
e.Appearance.BackColor = backColor;
}
catch (Exception ex)
{
XtraMessageBox.Show(ex.Message);
}
}
public string GetBackColor(byte actionTypeID)
{
string color = string.Empty;
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[DivaSqlSiteConnString].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand(#"Select BackColor from ActionTypes where ID = #actionTypeID"))
{
SqlParameter param = new SqlParameter("#actionTypeID", actionTypeID);
cmd.Parameters.Add(param);
cmd.Connection = conn;
conn.Open();
color = cmd.ExecuteScalar().ToString();
conn.Close();
}
}
return color;
}
My best guess is that some part of your code is just really slow.
The event only fires for each visible cell in the grid. If you attempt to debug the event, focus will shift to the debugger, and when you return to the application the cells need to be redrawn, causing the event to fire again, thus giving the impression that the event fires continuously. It does not, however.
Here are some pointers to improve performance:
You are constructing a new DivaDs every time the event fires
Instead, consider reusing the same instance of the class as a member variable
What happens in the constructor?
Take a closer look at the GetBackColor method or ColorTranslator.FromHtml and see if any modifications can be made to improve performance.
Update
It appears you are querying the database for each cell in the grid. This is a really bad idea.
A simple solution would be to preload all ActionTypes and their background colors (or at least the subset of ActionTypes that is displayed in the grid) before setting the grid's data source.
// member variable
private Dictionary<byte, Color> actionTypeColorDict;
void BuildActionTypeColorDictionary()
{
string connectionString = ConfigurationManager
.ConnectionStrings[DivaSqlSiteConnString].ConnectionString;
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand cmd = conn.CreateCommand())
using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
{
// load all action type IDs and corresponding background color:
cmd.CommandText = #"SELECT ActionTypeID, BackColor FROM ActionTypes";
DataTable actionTypeTable = new DataTable();
adapter.Fill(actionTypeTable);
// build a dictionary consisting of action type IDs
// and their corresponding colors
actionTypeColorDict = actionTypeTable.AsEnumerable().ToDictionary(
r => r.Field<byte>("ActionTypeID"),
r => ColorTranslator.FromHtml(r.Field<string>("ColorCode")));
}
}
Call the BuildActionTypeColorDictionary method before setting the data source of the grid. In the RowStyle or CustomDrawCell events, use the new dictionary member to determine the background color. See the following modified version of your RowStyle code:
private void gvStep_RowStyle(object sender,DevExpress.XtraGrid.Views.Grid.RowStyleEventArgs e)
{
try
{
DataRow row = gvStep.GetDataRow(e.RowHandle);
if (row == null)
return;
byte actionTypeID = row.Field<byte>("ActionImage");
// look up color in the dictionary:
e.Appearance.BackColor = actionTypeColorDict[actionTypeID];
}
catch (Exception ex)
{
XtraMessageBox.Show(ex.Message);
}
}
How do you know it's firing continuously? Are you debbuging?
This code runs whenever the grid is redrawn, meaning whenever the form gets focus.
This event runs for each cell - so it will run quite a few times.
If you put a break-point in this event you'll never get out of it. It will break, you will debug, when it's done it will return focus to the form - causing the form to be redrawn using this event and the break-point is reached again.
And just a side note - Whenever I use that event I have to put e.Handled = true; in the code so that the cell isn't "drawn" by anyone but me :)
Finally, I found it. RowStyle event only fires same time with gridview's row count
private void gvStep_RowStyle(object sender, DevExpress.XtraGrid.Views.Grid.RowStyleEventArgs e)
{
try
{
DataRowView drw = (DataRowView)gridView1.GetRow(e.RowHandle);
if (drw == null)
return;
byte actionTypeID = (byte)drw.Row["ActionImage"];
string colorCode = divaDs.GetBackColor(actionTypeID);
Color backColor = ColorTranslator.FromHtml(colorCode);
e.Appearance.BackColor = backColor;
}
catch (Exception ex)
{
XtraMessageBox.Show(ex.Message);
}
}
I have a Silverlight app. that has a basic animation where a rectangle is animated to a new position. The animation consists of two DoubleAnimation() - one transforms the X, the other transforms the Y. It works OK.
I basically want to block any other calls to this animate method until the first two animations have completed. I see that the DoubleAnimation() class has a Completed event it fires but I haven't been successful in constructing any kind of code that successfully blocks until both have completed.
I attempted to use Monitor.Enter on a private member when entering the method, then releasing the lock from one of the animations Completed event, but my attempts at chaining the two events (so the lock isn't released until both have completed) haven't been successful.
Here's what the animation method looks like:
public void AnimateRectangle(Rectangle rect, double newX, double newY)
{
var xIsComplete = false;
Duration duration = new Duration(new TimeSpan(0, 0, 0, 1, 350));
var easing = new ElasticEase() { EasingMode = EasingMode.EaseOut, Oscillations = 1, Springiness = 4 };
var animateX = new DoubleAnimation();
var animateY = new DoubleAnimation();
animateX.EasingFunction = easing;
animateX.Duration = duration;
animateY.EasingFunction = easing;
animateY.Duration = duration;
var sb = new Storyboard();
sb.Duration = duration;
sb.Children.Add(animateX);
sb.Children.Add(animateY);
Storyboard.SetTarget(animateX, rect);
Storyboard.SetTargetProperty(animateX, new PropertyPath("(Canvas.Left)"));
Storyboard.SetTarget(animateY, rect);
Storyboard.SetTargetProperty(animateY, new PropertyPath("(Canvas.Top)"));
animateX.To = newX;
animateY.To = newY;
sb.Begin();
}
EDIT (added more info)
I ran into this initially because I was calling this method from another method (as it processed items it made a call to the animation). I noticed that the items didn't end up where I expected them to. The new X/Y coordinates I pass in are based on the items current location, so if it was called multiple times before it finished, it ended up in the wrong location. As a test I added a button that only ran the animation once. It worked. However, if I click on the button a bunch of times in a row I see the same behavior as before: items end up in the wrong location.
Yes, it appears Silverlight animations are run on the main UI thread. One of the tests I tried I added two properties that flagged whether both animations had completed yet. In the AnimateRectange() method I checked them inside of a while loop (calling Thread.Sleep). This loop never completed (so it's definitely on the same thread).
So I created a queue to process the animations in order:
private void ProcessAnimationQueue()
{
var items = this.m_animationQueue.GetEnumerator();
while (items.MoveNext())
{
while (this.m_isXanimationInProgress || this.m_isYanimationInProgress)
{
System.Threading.Thread.Sleep(100);
}
var item = items.Current;
Dispatcher.BeginInvoke(() => this.AnimateRectangle(item.Rect.Rect, item.X, item.Y));
}
}
Then I call my initial routine (which queues up the animations) and call this method on a new thread. I see the same results.
As far as I am aware all of the animations in Silverlight are happening on the UI thread anyway. I am guessing that only the UI thread is calling this animation function anyway, so I am not sure that using locking will help. Do you really want to be blocking the entire thread or just preventing another animation from starting?
I would suggest something more like this:
private bool isAnimating = false;
public void AnimateRectangle(Rectangle rect, double newX, double newY)
{
if (isAnimating)
return;
// rest of animation code
sb.Completed += (sender, e) =>
{
isAnimating = false;
};
isAnimating = true;
sb.Begin();
}
Just keep track of whether or not you are currently animating with a flag and return early if you are. If you don't want to lose potential animations your other option is to keep some kind of a queue for animation which you could check/start when each animation has completed.
This question really peaked my interest. In fact I'm going to include it in my next blog post.
Boiling it down, just to be sure we are talking about the same thing, fundementally you don't want to block the call to AnimateRectangle you just want to "queue" the call so that once any outstanding call has completed its animation this "queued" call gets executed. By extension you may need to queue several calls if a previous call hasn't even started yet.
So we need two things:-
A means to treat what are essentially asynchronous operations (sb.Begin to Completed event) as a sequential operation, one operation only starting when the previous has completed.
A means to queue additional operations when one or more operations are yet to complete.
AsyncOperationService
Item 1 comes up in a zillion different ways in Silverlight due to the asynchronous nature of so many things. I solve this issue with a simple asynchronous operation runner blogged here. Add the AsyncOperationService code to your project.
AsyncOperationQueue
Its item 2 that really took my interest. The variation here is that whilst an existing set of operations are in progress there is demand to add another. For a general case solution we'd need a thread-safe means of including another operation.
Here is the bare-bones of a AsyncOperationQueue:-
public class AsyncOperationQueue
{
readonly Queue<AsyncOperation> myQueue = new Queue<AsyncOperation>();
AsyncOperation myCurrentOp = null;
public void Enqueue(AsyncOperation op)
{
bool start = false;
lock (myQueue)
{
if (myCurrentOp != null)
{
myQueue.Enqueue(op);
}
else
{
myCurrentOp = op;
start = true;
}
}
if (start)
DequeueOps().Run(delegate { });
}
private AsyncOperation GetNextOperation()
{
lock (myQueue)
{
myCurrentOp = (myQueue.Count > 0) ? myQueue.Dequeue() : null;
return myCurrentOp;
}
}
private IEnumerable<AsyncOperation> DequeueOps()
{
AsyncOperation nextOp = myCurrentOp;
while (nextOp != null)
{
yield return nextOp;
nextOp = GetNextOperation();
}
}
}
Putting it to use
First thing to do is convert your existing AnimateRectangle method into a GetAnimateRectangleOp that returns a AsyncOperation. Like this:-
public AsyncOperation GetAnimateRectangleOp(Rectangle rect, double newX, double newY)
{
return (completed) =>
{
// Code identical to the body of your original AnimateRectangle method.
sb.Begin();
sb.Completed += (s, args) => completed(null);
};
}
We need to hold an instance of the AsyncOperationQueue:-
private AsyncOperationQueue myAnimationQueue = new AsyncOperationQueue();
Finally we need to re-create AnimateRectangle that enqueues the operation to the queue:-
public void AnimateRectangle(Rectangle rect, double newX, double newY)
{
myAnimationQueue.Enqueue(GetAnimateRectangleOp(rect, newX, newY)
}