I want to create a regret button, kind of like in the tinder app.
I want this button to look at the last removed user from the array, and put it back into view.
I have tried with this code, which adds to user back to the queue, but not when pressing. The user will be put at index 0, which is next in line. If I switch out the index, the app crashes so I assume another way has to be done.
Furthermore, the button can be pressed multiple times, and the user will appear multiple times in the view. You can see the commented code in the if statement, where I tried to solve this
fabRegret.setOnClickListener {
if (rowItems.size != 0)//&& rowItems[0] != lastDeleted)
rowItems.add(0, lastDeleted!!)
Toast.makeText(getContext(), "User added to queue", Toast.LENGTH_LONG).show()
cardAdapter!!.notifyDataSetChanged() //undo button can be pressed multiple times to add same person again
}
For clarifitcation:
rowItems = ArrayList()
lastDeleted = rowItems.removeAt(0)
First, I would please you to ask questions more precisely. I'm not sure if the following answer can help to solve your problem.
As both commentators already said, there are multiple ways to implement an Undo / History Behaviour.
1. Design Pattern
I recommend you to use the command design pattern, as acarlstein already suggested.
Here is another link I can recommend.
2. Stack (duplicates)
If you want to implement it the quick & easy way, you could use a Stack instead of an ArrayList.
It's a LIFO (Last In - First Out) Storage.
You can implement the Stack (can hold duplicates) as follows:
val stack = stackOf(itemOne, itemTwo, itemThree)
stack.push(itemFour)
val item = stack.pop() // itemFour
But as I understand your question, you don't want to have duplicates in your history.
3. Stack adaption (no duplicates)
Without duplicates, you can adapt the Stack and override it's push() method to avoid duplicates.
Like this:
class UniqueStack<E> : Stack<E>() {
override fun push(item : E) : E {
// when item is already part of the stack, push it to the top
if (contains(item)) remove(item)
return super.push(item)
}
}
An implementation could look like:
val history = UniqueStack<Int>()
history.push(1) // [1]
history.push(1) // [1]
history.push(2) // [1, 2]
history.push(1) // [2, 1]
history.push(1) // [2, 1]
println(history) // prints "[2, 1]"
println(history.pop()) // prints "1"
println(history) // prints "[2]"
I hope I was able to help you.
Related
I wrote in code comments primarily whats going on with the code but here is a little more indepth. I created an array of drawable resources (images). I created a button and assigned its function to switch to the next element of the array with a click. ( Once its at the last element I intend to have it go back to element[0] but I haven't gotten that far yet).
I'm pretty new to android development so I hope this isn't a silly question. when I run the app and click the button, it jumps to the very last element. I also noticed it does not even show the first element on the screen ( just blank, then on a button click you see last element). below is the picture of whats happening:
p.s. there used to be more array elements but I deleted them to see if they were the problem.
p.s.s
here is the actual code
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.ImageView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//here I created an array of drawable images to pick from for the hair options
val blankHairStyleList = arrayOf(R.drawable.hairponytailpink , R.drawable.hairponytailblue)
//here I created an iterator which allows me to move through the array.
val blankHairStyleListIterator = blankHairStyleList.iterator()
//created button's click action
findViewById<Button>(R.id.HairButton).setOnClickListener{
//here is the actual iterator function, I feel like something is missing above here.
while(blankHairStyleListIterator.hasNext()){
findViewById<ImageView>(R.id.picture).setBackgroundResource(blankHairStyleListIterator.next())
}
}
photo of whats happeneing
This is a great start! Here's how you can fix it up.
I created a button and assigned its function to switch to the next element of the array with a click.
What you have right now will always cycle to the end because the while loop will keep running as long as #hasNext is true.
while (blankHairStyleListIterator.hasNext()) {
findViewById<ImageView>(R.id.picture).setBackgroundResource(blankHairStyleListIterator.next())
}
If you change the while to an if, the block will only run once!
if (blankHairStyleListIterator.hasNext()) {
findViewById<ImageView>(R.id.picture).setBackgroundResource(blankHairStyleListIterator.next())
}
Once its at the last element I intend to have it go back to element[0] but I haven't gotten that far yet.
Once you have that if statement, you can use the else to create a new iterator (after you make the blankHairStyleListIterator a var instead of a val).
if (blankHairStyleListIterator.hasNext()) {
findViewById<ImageView>(R.id.picture).setBackgroundResource(blankHairStyleListIterator.next())
} else {
findViewById<ImageView>(R.id.picture).setBackgroundResource(blankHairStyleList.first())
blankHairStyleListIterator = blankHairStyleList.iterator()
}
I also noticed it does not even show the first element on the screen (just blank, then on a button click you see last element).
You need to set the element on the ImageView. You can put something like this before the setOnClickListener.
findViewById<ImageView>(R.id.picture).setBackgroundResource(blankHairStyleList.first())
Please let me know if this does the trick for you!
You want something else as per your requirement and your code is doing a different thing.
As per your code, you are setting image background until your iterator has reached last item as below -
findViewById<Button>(R.id.HairButton).setOnClickListener{
while(blankHairStyleListIterator.hasNext()){
findViewById<ImageView>(R.id.picture).setBackgroundResource(blankHairStyleListIterator.next())
}
}
Because you are using while so code will set last item as background by skipping all of the previous items.
You need to make change in your code as per your requirement as below -
findViewById<Button>(R.id.HairButton).setOnClickListener{
if(blankHairStyleListIterator.hasNext()){
findViewById<ImageView>(R.id.picture).setBackgroundResource(blankHairStyleListIterator.next())
}
}
I have an android application that asks users a few questions, kind of a survey. Each question has an YES and a NO Button. So when the user presses either YES or NO button, a new Button to go to the next question becomes visible over the YES and NO buttons. When the user clicks on the next question button it changes the textview for the question to the next question text as well as the imageView for that question changes.
I am making use of DataBinding along with LiveData.
I have created a list of images used and another list for the questions as follows in my ViewModel class.:
//list of images : Question 4 does not have an image
imgs= listOf(
R.drawable.imgQ1, // 1
R.drawable.imgQ2,// 2
R.drawable.imgQ3)//3
//list of questions
qs= listOf(
"What is .....?",//1
"Who is ....?",//2
"How to .....?",//3
"Why is .....?")//4
I created a ViewState to handle when to show which button and UI:
sealed class MyUIState{
data class UIState(
val policyText: Boolean, // determines if the policy textview should be visible or not
val nextQuestionButton: Boolean,// determines if the next question button should be visible or not
val nextQuestionNewImage: Int = R.drawable.imgQ1,//what image is used for the next question
val nextQuestion: String //what is the next question from the list
) : MyUIState()
}
I also have a BindingAdapter class which handles the visibility and text changes and image changes
(basically this - I have added these function to my layout file with data binding):
#BindingAdapter("btnNextQ")
fun Button.btnNextQ(myState: MyUIState?){
visibility = if (myState is MyUIState.UIState) {
if (state.nextQuestionButton)
View.VISIBLE
else
View.GONE
} else
visibility
}
#BindingAdapter("newImage")
fun ImageView.newImage(myState: MyUIState?){
if(myStateis MyUIState.UIState){
setImageResource(state.nextQuestionNewImage)
}
}
And then in my ViewModel class I have a function for the click event on either the YES or NO Button where I call the UIState like this:
//I have this declared at the top of my class (global)
private val myViewState = MutableLiveData<MyUIState>()
//this the button click for yes:
fun YesClick(){
myViewState.postValue(MyUIState.UIState(
policyText= true,
nextQuestionButton = true,
nextQuestion = qs[0])) //still keep the text for question1
}
Now, I want the image and question to change after the user clicked the next question button. So basically what happens is that after clicking next question, the next question buttons goes invisible as well as the other components keeping the YES and NO buttons and the updated question and image. This is my code for the click event on the next question button:
fun nextQuestion(){
myViewState .postValue(MyUIState.UIState(
nextQuestionNewImage = imgs[1],
nextQuestion = qs[1],
nextQuestionButton = false,
policyText= false))
}
So the state does move to the next question with the necessary items being visible and invisible, but I am struggling to keep it going to the next questions in the list. I can go from question1 to question2 but not sure how to keep doing that until the end of the list of questions.
So basically, I just want to keep updating the same activity with the new data.
Any help or advice will be highly appreciated.
Not sure if I understood your real problem. Maybe if you share more of your code we could help you better.
Anyway, I would recommend you to have two states for your view (activity in your case), one state for the question itself containing both yes and no buttons, question text and the right image. And another state for your "move to next" question which contains the question text, button and whatever you want.
Then your view needs to take care of each state. In the case, you need to deal with one state while dealing against the other state, for example showing one button and hiding the ones you don't need.
So whenever you click on the yes button, the VM will get the event, check everything and then emit an event back to the View with a new state, and so on.
It's quite the same thing you did, but using two states will help you to tackle each state and ease off your code
I'm using SelectionTracker form the support-library-v28
It works great, just as expected.
Only thing I need, is to enable Select All feature (using the ToolBar)
Looking at the API, I see that there is one way to select all, but that one requires creating Iterable<Long> with all the values, meaning, create an array which hold Long values from 1 to datasource.size()
Is there any simpler way to select all the items in my datasource?
Seeing as I've recently had to set up the same sort of functionality, I thought I'd share my approach. Using the setItemsSelected(Iterable<K> keys, boolean selected) method really isn't as complex as it seems.
Yes, you will need to pass in an iterable. What I did was, loop through my data and store the index of each item as a 'long' inside of an arrayList().
EX:
yourData.forEachWithIndex { i, item ->
//Be sure to start at one, just plus one
someOtherTempArray.add(i.toLong() + 1)
}
Then I created a method that makes it easy to 'trigger' the select all functionality:
EX:
private fun startHandler(isChecked: Boolean){
val handler = android.os.Handler(Looper.getMainLooper())
val runnable = Runnable {
kotlin.run {
mTracker!!.setItemsSelected(someOtherTempArray.asIterable(),
isChecked)
}
}
handler.post(runnable)
}
The 'isChecked' parameter, which will essentially determine if we want to select all or deselect all.
Note that I am simply using my arrayList of 'keys' (for me this is just the index of the item. However, this may differ depending on how you have your ItemDetails Builder set up) and calling the Kotlin .asIterable() function to turn it into an iterable.
I have also placed the call to setItemsSelected() inside of a handler to force the selection to take place on the UI thread.
Now whenever you want to select/deselect all, you can call the startHandler(true)!
I'm new to Android and I'm building a simple application to start with. It consists of a client with three screens. In the first screen the user is prompted for an Ip to connect to a server (I use an EditText and a button). If the connection is successfully established, some data will be retrieved from the server and the client will show the data on a blank screen (I use a TextView). This would be the second screen. Then, the user could ask the server for detailed information about any data that has been retrieved from the server, which would be the third screen (I use a TextView again).
The problem is that I don't know what's the best way to go about it. I have currently one activity and one XML file containing all the components of the view (EditText, button, TextView). Until now, I've been using setVisibility(View.GONE);to hide certain components depending on the screen the user is in. (For example in the first screen I have to hide both TextViews).
One of the problems I'm facing is that when I put the phone in a horizontal position the components I had hidden show up again. I don't know if hiding views is the ideal thing to do for my purpose.
I've thought that maybe I should use more than one activity, shouldn't I?
I really appreciate any help you can give me to structure my first app.
I would definitely recommend splitting up your App into multiple Activities/Fragments. Depending on how big the logic for each screen gets you will be glad you did it later on because each Activity only has one responsibility.
Look at your mail app for example. You got your List Activity showing you all your mails and then when you select one it starts the Detail Activity showing you the content of your mail. Each Activity is only responsible for one thing which make each one easier to write and maintain.
It also simplifies your layout definitions because each one only contains the relevant parts.
Seems like this is coming up a lot. Android destroys and recreates and Activity when the configuration changes. Screen rotation is part of the orientation. In order to avoid that, the Activity is responsible for retaining state. The mechanisms given for that are the onCreate and onSaveInstanceState. In your example, you could do something like the following:
int uiPhase = 1;
#Override
void onCreate( Bundle data ) {
uiPhase = data.getInt( "uiPhase", 1 );
// inflate layout
setPhase( uiPhase );
}
// invoke the following each time your screen changes
void setPhase( int newPhase ) {
uiPhase = newPhase;
switch( uiPhase ) {
case 1: // show UI elements for first screen, hide others
break;
case 2: // show UI elements for second screen, hide others
break;
case 3: // show UI elements for third screen, hide others
break;
}
}
#Override
void onSaveInstanceState( Bundle data ) {
data.put( "uiPhase", uiPhase );
}
I didn't want to complicate the pattern above too much, but a good method for setting visibility is as follows:
phase1view.setVisibility( uiPhase == 1 ? View.VISIBLE : View.GONE );
phase2view.setVisibility( uiPhase == 2 ? View.VISIBLE : View.GONE );
phase3view.setVisibility( uiPhase == 3 ? View.VISIBLE : View.GONE );
That pulls the repetition in the setPhase method quite a bit together.
Set button visibility to GONE (button will be completely "removed" -- the buttons space will be available for another widgets) or INVISIBLE (button will became "transparent" -- its space will not be available for another widgets):
use in place of
setVisibility(View.GONE)
change to
setVisibility(View.INVISIBLE) and try
I have an image which I am processing, and I have two buttons, undo and redo. I need the code to undo/redo previous touch action if either of those two buttons are clicked. I know I have to use a stack. How should I implement it?
There are two main patterns for implementing Undo/Redo:
The "memento" pattern.
The "command" pattern.
1. Memento Pattern
The idea of the memento pattern is that you can save a copy of the entire internal state of an object (without violating encapsulation) to be restored later.
It would be used (for example) like this:
// Create your object that can be "undone"
ImageObject myImage = new ImageObject()
// Save an "undo" point.
var memento = myImage.CreateMemento();
// do a bunch of crazy stuff to the image...
// ...
// Restore to a previous state.
myImage.SetMemento(memento);
2. Command Pattern
The idea of the command pattern is to encapsulate the actions that are actually performed on an object. Each "action" (or, "command") can optionally know how to roll itself back. Alternatively, when a rollback needs to occur, the entire chain of commands can be executed again.
It would be used (for example) like this:
// Create your object that can be "undone"
ImageObject myImage = new ImageObject()
// Create a "select all" command
var command = new SelectAllCommand(myImage); // This does not actually execute the action.
// Apply the "select all" command to the image
selectAll.Execute(); // In this example, the selectAll command would "take note" of the selection that it is overwriting.
// When needed, rollback:
selectAll.Rollback(); // This would have the effect of restoring the previous selection.
It all depends what your touch events do in the first place. You have to abstract what your application does in response to the touches into a class that you can fill a Stack with. Then, the stack implementation is easy.
If it's image manipulation, it might take up too much memory to keep a whole stack of Bitmaps around. You'll probably get the infamous OutOfMemoryException after pushing two or three items onto your stack. What you'd probably be better off doing is abstract the actions available in your app and rebuilding on undo/redo. You're basically creating a stack of instruction sets. This makes it slower the larger your stack is, but if the images in memory are large it might be the only way to do it.
In the newer Android versions (22+) you could use a Snackbar. Here's small code fragment for listener:
public class MyUndoListener implements View.OnClickListener{
&Override
public void onClick(View v) {
// Code to undo the user's last action
}
}
and creating a message at the bottom of the screen for an "undo" action:
Snackbar mySnackbar = Snackbar.make(findViewById(R.id.myCoordinatorLayout),
R.string.email_archived, Snackbar.LENGTH_SHORT);
mySnackbar.setAction(R.string.undo_string, new MyUndoListener());
mySnackbar.show();