Flashcards Test/Memorization in Andoird Studio/Kotlin - arrays

Sorry for the long post! I am trying to make a memorization flashcard and I have been stuck with this for a week
Here is the array
val sentenceArray = arrayOfNulls<String>(8)
sentenceArray[0] = ""
sentenceArray[1] = "Array 1 (1)"
sentenceArray[2] = "Array 2 (2)"
sentenceArray[3] = "Array 3 (3)"
sentenceArray[4] = "Array 4 (4)"
sentenceArray[5] = "Array 5 (5)"
This is the code. To generate the test there are 2 spinners (left and right) If the user selected 1 for the left spinner and 3 on the right spinner then it will only display Array 1 to 3 each time the user clicks on textView
leftSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener
{
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long)
{
//Stores value of the selected Left Spinner
val selectedLeftSpinner = leftSpinner.getItemAtPosition(position).toString()
rightSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener
{
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long)
{
//Actions when buttonStart is clicked
buttonStart.setOnClickListener {
//Stores value of the selected Right Spinner
val selectedRightSpinner = rightSpinner.getItemAtPosition(position).toString()
//Stores selectedLeftSpinner value to firstSentence so that it can be used
var firstSentence = selectedLeftSpinner.toInt()
//Stores selectedRightSpinner value to lastSentence so that it can be used
val lastSentence = selectedRightSpinner.toInt()
//Displays (1)
textView.text = sentenceArray[firstSentence]?.takeLast(3)
//Waiting for an action on the TextView
textView.setOnClickListener {
//Displays (Array 1)
textView.text = sentenceArray[firstSentence]
//Waiting for an action on the TextView
textView.setOnClickListener {
while (firstSentence < lastSentence) {
//This while loop displays Array 1 to 5 each time a user clicks on textView
firstSentence++
textView.text = sentenceArray[firstSentence]
//Ends the test when it reaches the last array
if (firstSentence == lastSentence) {
buttonStart.text = "DONE"
}
break
}
}
}
}
}
}
}
}
What I am trying to do:
After the user selected a value from the left spinner (for example 1) and right spinner (for example 3) They would click on the buttonStart to start the flashcard. The textView starts off displaying a (1). To reveal the answer, they could click on textView and then it will display "Array 1" They would then click on textView and it will display a (2)... etc
So the flow should be like this, after clicking on textView on each step
(1)
Array 1
(2)
Array 2
(3)
Arra3 3
DONE
With my current while loop
while (firstSentence < lastSentence) {
//This loop displays Array 1 to 5 each time a user clicks on textView
firstSentence++
textView.text = sentenceArray[firstSentence]
//Ends the test when it reaches the last aya
if (firstSentence == lastSentence) {
buttonStart.text = "DONE"
}
break
}
This is the current flow
(1)
Array 1
Array 2
Array 3
DONE
I have tried a lot of things but I could not get it to work and I have no idea way
Here are 2 different codes that I tried that did not work. This was inside the while loop
textView.setOnClickListener {
if (textView.text == sentenceArray[firstSentence])
textView.text = sentenceArray[firstSentence]?.takeLast(3)
}
and this code
if (firstSentence < lastSentence)
{
textView.setOnClickListener {
textView.text = sentenceArray[firstSentence]?.takeLast(3)
}
}
The 2 codes result into this flow
(1)
Array 1
Array 2
(2)
DONE when its supposed to continue to (3)

Your dont really need a loop, since looping is handled by the user clicking the text view, in your case an if condition will suffice.
One other thing that I observed is that you are setting up your click listeners and spinner listeners inside other listeneres. this is not required and makes your code unreadable and complex to parse, Please note that in your case you can define all the listeners on the same level to achieve what you want, you dont have to nest them.
You can do something as following. I have lifted out the nested listener configuration.
class Temp {
lateinit var leftSpinner: Spinner
lateinit var rightSpinner: Spinner
lateinit var buttonStart: Button
lateinit var sentenceArray: Array<String>
lateinit var textView: TextView
var selectedLeftSpinner = -1
var selectedRightSpinner = -1
private fun setup() {
/** Define left spinner item selected listener, capture the selected position */
leftSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(
parent: AdapterView<*>?, view: View?,
position: Int, id: Long) {
selectedLeftSpinner = position
}
}
/** Define right spinner item selected listener, capture the selected position */
rightSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(
parent: AdapterView<*>?, view: View?,
position: Int, id: Long) {
selectedRightSpinner = position
}
}
/** Define button start click listener, show text if left spinner is selected. */
buttonStart.setOnClickListener {
/** only set text if left spinner is selecte */
if(selectedLeftSpinner != -1){
textView.text = sentenceArray[selectedLeftSpinner]?.takeLast(3)
}
}
/** Handle edge cases, such as what happens if spinner is not selected */
textView.setOnClickListener {
/** if currently selected item's last 3 chars are being shown,
* then show the answer.
*/
if(textView.text == sentenceArray[selectedLeftSpinner]?.takeLast(3)){
textView.text = sentenceArray[selectedLeftSpinner]
}
/** if answer is being shown then show last three chars of next item */
else if(selectedLeftSpinner < selectedRightSpinner){
selectedLeftSpinner++
textView.text = sentenceArray[selectedLeftSpinner]?.takeLast(3)
}
/** else we have reached the last item */
else{
buttonStart.text = "DONE"
/** reset left spinner to not selected state */
selectedLeftSpinner = -1
}
}
}
}
This should work but I have not considered any edge cases, so you will have to take care of them and there is space for other improvments such as usig a key, value structure(ex map) to store your flash card question and answers an code can still be reduced as both of listeners for spinners are identical, I leave it as an exercise.

Related

Script to Change selected item to a Swatch Color

I have a basic JavaScript that should change the selected item from CMYK Black to a Swatch Color Named Bronze but it's not working. Any advice on haw the code should look?
// Get the active document
var doc = app.activeDocument;
// Check if there is a selection
if (doc.selection.length > 0) {
// Get the first selected object
var obj = doc.selection[0];
// Check if the object has a fill
if (obj.fillColor > 0) {
// Set the object's fill color to the "Bronze" swatch
obj.fillColor = 5;
}
}
Change your code to this, this works, I have tested it.
// Get the active document
var doc = app.activeDocument;
// Check if there is a selection
if (doc.selection.length > 0) {
// Get the first selected object
var obj = doc.selection[0];
//change your code to lines below ---v
if (obj.filled)
obj.fillColor = activeDocument.swatches["Bronze"].color
}

How can Jetpack Compose Navigation remember multiple back stacks?

I'm reading about Navigation in Jetpack Compose, and found this example I don't understand.
From the docs:
By using the saveState and restoreState flags, the state and back stack of that item is correctly saved and restored as you swap between bottom navigation items.
val navController = rememberNavController()
Scaffold(
bottomBar = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
items.forEach { screen ->
BottomNavigationItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
label = { Text(stringResource(screen.resourceId)) },
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = true
}
}
)
}
}
}
) { innerPadding ->
NavHost(navController, startDestination = Screen.Profile.route, Modifier.padding(innerPadding)) {
composable(Screen.Profile.route) { Profile(navController) }
composable(Screen.FriendsList.route) { FriendsList(navController) }
}
}
Specifically, I don't understand how the back stack can be saved if clicking an item in the bottom bar pops the navigation stack to the root.
I would imagine a journey like:
User moves to /FriendsList/Friend(A)/Friend(B)/Friend(C)
User clicks Profile button, resetting the navigation stack to /Profile
User clicks FriendsList button.
Based on the explanation, I would expect the navigation stack to be re-set to /FriendsList/FriendA/FriendB/FriendC, even though the onClick listener seems to set the stack to /FriendsList?
I really don't understand how this can happen, how does the navigation controller link the route to the entire navigation sub-stack? Is item.route changing state containing the full route to /FriendsList/Friend(A)/Friend(B)/Friend(C), or is something else going on? Or do I understand the example wrong?
I suspect maybe the underlying mechanism is that FriendsList contains a nested navigation graph, since the example doesn't actually show any Friend route definitions. The state of this entire nested graph is contained somehow, i.e., something like /FriendsList{FriendA/FriendB/FriendC}, and a move to /FriendsList will unpack this navigation stack. Is that kind of how it works?
The code in the example you gave has only 1 navHost and 1 navController so it is not possible to save the FriendList back stack seperated from the rest back stack.
To achive this behavior you need to declare nested navHosts. Each of them with his own navController and thats it. All the rest is working automatically.
Here is an example
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
Scaffold(
bottomBar = {
BottomNavigation {
items.forEach { screen ->
BottomNavigationItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
label = { Text(stringResource(screen.resourceId)) },
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = true
}
}
)
}
}
}
) { innerPadding ->
NavHost(
navController,
startDestination = Screen.ProfileNavHost.route,
Modifier.padding(innerPadding)
) {
composable(Screen.ProfileNavHost.route) {
val profileNavController = rememberNavController()
NavHost(
navController = profileNavController,
startDestination = Screen.ProfileRoot.route
) {
composable(Screen.ProfileRoot.route) {
ProfileRoot(navController)
}
composable(Screen.ProfileScreen.route) {
ProfileScreen(navController)
}
}
}
composable(Screen.FriendsListNavHost.route) {
val friendsListNavController = rememberNavController()
NavHost(
navController = friendsListNavController,
startDestination = Screen.FriendsListRoot.route
) {
composable(Screen.FriendsListRoot.route) {
FriendsList(navController)
}
composable(Screen.FriendsListFriend.route) {
FriendsListFriend(navController)
}
}
}
}
}

Typescript: Array not storing objects

I'm trying to assign row object to an array from a check box click event. For this i have created a global array like below.
SelectedGrid: any[] = [];
At the moment of clicking it seems it going into the array, but when i checked the second checkbox 1st item is gone missing from the array.
May I know why that happens?
following is the function i call in check box click event.
public clickConditionRow(row, col, rowSelected) {
if (rowSelected.isChecked) {
this.SelectedGrid.push(rowSelected);
console.dir(this.SelectedGrid);
console.log(this.SelectedGrid.length);
} else {
console.log("Unselected ");
var toDel = this.SelectedGrid.indexOf(rowSelected);
this.SelectedGrid.splice(toDel);
console.dir(this.SelectedGrid);
}
if (!this.rootScope.$$phase) {
this.rootScope.$apply();
}
// for print the values
for (var y = 0; y < this.SelectedGrid.length; y++) {
console.log("Element " + y + " = " + this.SelectedGrid[y]);
console.dir(this.SelectedGrid[y]);
}
}
I cannot reproduce your bug (maybe you are overriding the array somewhere else?) but I see 2 potential problems and want to point them out:
Using indexOf with and object might not work and return -1 (see the code below)
splice(toDel) should be splice(toDel, 1) or it will delete all items staring from index toDel to the end of the array.
const a = { id: 1 }
const b = { id: 2 }
const list = [ a, b ]
// This returns 1
console.log(list.indexOf(b))
// This returns -1
console.log(list.indexOf({ id: 2 }))

iOS Swift - How to get the last items of an array and append it to another array for chat messages

I have the following code (a Parse Server query into a function that gets fired every 20 seconds and checks if there are new rows in the Messages class (table)):
/* Variables */
var messagesArray = [PFObject]()
var theMessages = [PFObject]()
/* func queryMessages() code */
let messId1 = "\(currentUser.objectId!)\(userObj.objectId!)"
let messId2 = "\(userObj.objectId!)\(currentUser.objectId!)"
let predicate = NSPredicate(format:"messageID = '\(messId1)' OR messageID = '\(messId2)'")
let query = PFQuery(className: MESSAGES_CLASS_NAME, predicate: predicate)
query.whereKey(MESSAGES_DELETED_BY, notContainedIn: [currentUser.objectId!])
query.order(byAscending: "createdAt")
query.skip = skip
query.findObjectsInBackground { (objects, error)-> Void in
if error == nil {
for i in 0..<objects!.count { self.messagesArray.append(objects![i]) }
if (objects!.count == 100) {
self.skip = self.skip + 100
self.queryMessages()
} else {
self.messagesArray = objects!
/*test*/
let messDiff = self.messagesArray.count - self.theMessages.count
print("MESSAGES ARRAY: \(self.messagesArray.count)")
print("THE MESSAGES: \(self.theMessages.count)")
print("MESS DIFF: \(messDiff)\n")
if self.theMessages.count < self.messagesArray.count {
for i in 0..<messDiff {
self.theMessages.append(self.messagesArray[i])
}// ./ For
self.messagesTableView.reloadData()
}// ./ If
// Scroll TableView down to the bottom
if objects!.count != 0 {
Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(self.scrollTableViewToBottom), userInfo: nil, repeats: false)
}
}// ./ If
// error
} else { self.simpleAlert("\(error!.localizedDescription)")
}}
So, the app is running on my device, and with another device I send 2 new messages to the test User on my device. After 20 seconds, a Timer calls the function above and I obviously get this in the console:
MESSAGES ARRAY: 9
THE MESSAGES: 7
MESS DIFF: 2
So, what I would need is to simply append the 2 new items of the messagesArray into the theMessages array, so that when my TableView will reload, it'll just add 2 new rows on its bottom.
Obviously now I get the first 2 messages again because of this code:
for i in 0..<messDiff {
self.theMessages.append(self.messagesArray[i])
}// ./ For
I cannot figure out what 'workaround' I should use to achieve what I need...
As you are reloading the entire table view without animations anyway why not simply
self.theMessages = self.messagesArray
self.messagesTableView.reloadData()
Or if the new messages are reliably at the end of the array get them with suffix and append them to theMessages
let newMessages = Array(self.messagesArray.suffix(messDiff))
self.theMessages.append(contentsOf: newMessages)
if self.theMessages.count < self.messagesArray.count {
let temp = self.theMessages.count
for i in 0..<messDiff {
self.theMessages.append(self.messagesArray[i+temp])
}// ./ For
self.messagesTableView.reloadData()}
change your code reload data code to this

Actionscript 3.0 : Controlling child movieclips added with an array

I'm trying to build a generic slide show flash file controlled with key presses... right and left arrow to go to next / previous slide, down and up to call / uncall the single animations / bullet points / whatever on a single slide.
So far I managed to get a file that can load all the pages from a movieclip and flick through them with right and left arrow and a nice fade out and fade in effect, even when flicking very fast.
What I do not get at all after several hours and lots of google searches is how to now control the single page movieclips. I uploaded the file so far:
http://www.broesel-brzelius.de/zeug/slide.zip
You can see that my (as of yet crude) attempt to control the pageHolder.Pages.page1 movieclip to gotoAndStop(2) just results in the pageHolder.Pages movieclip that was loaded to position 0 in the array to go to frame 2 and thus display the second page.
Code so far:
import flash.display.Sprite;
import flash.events.MouseEvent;
import com.greensock.*;
import flash.display.MovieClip;
// instantiate a variable to find number of pages
var numberOfPages:Pages = new Pages();
// instantiate an array to hold the page movieclips
var pageArray:Array = new Array();
// instantiate a container to hold the pages
var pageHolder:Sprite = new Sprite();
// declare variables that will hold reference to the current page IDs
var targetIDold:int = 0;
var targetIDnew:int = 0;
// declare a variable that will hold the current direction of slide movement
var movement:int = -1;
// call a function that builds the application
// pass in the number of pages in fl_prevSlide
buildApp(numberOfPages.totalFrames);
// this function builds the application
function buildApp(n:int):void
{
// declare variables for the pages
var p:Pages;
// instantiate a new Page, send its playhead to the current value of i+1
// it is necessary to add 1 to the value of i, since i starts at 0, while the timeline starts at 1
for (var i:int = 0; i < n; i++)
{
p = new Pages();
p.gotoAndStop(i + 1);
pageArray.push(p);
}
// set the position of the pageHolder relative to the buttonHolder
pageHolder.x = pageHolder.y = 0;
// add the first page (at index 0) from pageArray to pageHolder
pageHolder.addChild(pageArray[targetIDnew]);
// add pageHolder and buttonHolder to the stage
addChild(pageHolder);
}
//Adding Listener and corresponding function to change between slides
stage.addEventListener(KeyboardEvent.KEY_DOWN, f_changeSlide);
function f_changeSlide(evt:KeyboardEvent):void
{
if(evt.keyCode == 37) // left arrow
{
f_prevSlide();
}
else if (evt.keyCode == 39 || evt.keyCode == 32) // right arrow or space
{
f_nextSlide();
}
}
function f_prevSlide():void
{
if(targetIDnew > 0)
{
movement = -1;
targetIDnew -= 1;
f_addPage();
}
}
function f_nextSlide():void
{
if(targetIDnew < (numberOfPages.totalFrames - 1))
{
movement = 1;
targetIDnew += 1;
f_addPage();
}
}
function f_addPage():void
{
// use the targetID variable to access the corresponding index in the pageArray and assign it to a temporary variable
targetIDold = targetIDnew - movement;
var _mcOld:MovieClip = MovieClip(pageArray[targetIDold]);
var _mcNew:MovieClip = MovieClip(pageArray[targetIDnew]);
//avoid flickering
if (_mcNew.alpha == 1) {
_mcNew.alpha = 0;
}
// add the temp variable to pageHolder
pageHolder.addChild(_mcNew);
// tween the temp variable to the specified properties, then call a function to remove the previous page
// new page gets faded in, old page gets faded out
TweenMax.to(_mcNew, 1.5, {alpha:1});
TweenMax.to(_mcOld, 1.5, {alpha:0, onComplete:f_removePage});
}
function f_removePage():void
{
// previous page will always be at index 0; remove it
// the new page just added will drop down to index 0 when the previous page is removed
pageHolder.removeChildAt(0);
}
//Trying to advance / decrease the timeline in the movieclips of the single pages by pressing down / up
stage.addEventListener(KeyboardEvent.KEY_DOWN, f_changeFrame);
function f_changeFrame(evt:KeyboardEvent):void
{
if(evt.keyCode == 40) // down arrow
{
f_nextFrame();
}
else if (evt.keyCode == 38) // up arrow
{
f_prevFrame();
}
}
function f_nextFrame():void
{
var w:uint = pageHolder.numChildren - 1 ;
(pageHolder.getChildAt(w) as MovieClip).gotoAndStop(currentFrame + 1);
// for (var i:uint = 0; i < pageHolder.numChildren; i++)
// {
// trace (+i+'.\t name:' + pageHolder.getChildAt(i).name + '\t type:' + typeof (pageHolder.getChildAt(i)));
// }
trace ("down!");
}
function f_prevFrame():void
{
var w:uint = pageHolder.numChildren - 1 ;
(pageHolder.getChildAt(w) as MovieClip).gotoAndStop(currentFrame - 1);
// for (var i:uint = 0; i < pageHolder.numChildren; i++)
// {
// trace (+i+'.\t name:' + pageHolder.getChildAt(i).name + '\t type:' + typeof (pageHolder.getChildAt(i)));
// }
trace ("up!");
}
Edit: clarified code a bit
Edit2: TL;DR: Problem is the last bit of code with the three functions that fail to control the timeline of mc page1, which is a child of mc pages, which is pushed frame by frame into an array and loaded at runtime into the sprite pageHolder. How to control the timeline of the single page movieclips?
Finally got it to work. This will work as a perfect powerpoint replacement :D
You will need to name the single slides (one movieclip on each frame of the mc pages) as page0, page1 etc. and then can control their timeline with this code (just replace the functions listed in the question above):
function f_nextFrame():void
{
var temp:uint = pageHolder.numChildren - 1;
var frame:uint = ((pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].currentFrame);
(pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].gotoAndStop(frame + 1);
}
function f_prevFrame():void
{
var temp:uint = pageHolder.numChildren - 1;
var frame:uint = ((pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].currentFrame);
(pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].gotoAndStop(frame - 1);
}

Resources