I'm attempting to make my textView invisible for 4 seconds and then appear after using the code:
final TextView textView233 = findViewById(R.id.textView233);
textView233.setText("Loading data...");
textView233.setVisibility(View.VISIBLE);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
textView233.setVisibility(View.INVISIBLE);
}
}, 4000);
In the situation where I have over 100 textViews in my activity and I want ALL textviews to do the same, is there a way to apply it to all textViews other than writing the code for all 100 of them? Any help would be appreciated, thanks!
add all these TextView inside some LinearLayout and hide the LinearLayout layout after the given internal instead of hiding each and every TextView separately.
You can do it with a custom class:
AnimTextView.java
import android.os.Handler;
import android.view.View;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Arrays;
public class AnimTextView {
private ArrayList<TextView> textViewList;
public AnimTextView(TextView ... textViews) {
textViewList = new ArrayList<>();
textViewList.addAll(Arrays.asList(textViews));
}
public void startLoading(){
for (final TextView textView: textViewList) {
textView.setText("Loading data...");
textView.setVisibility(View.VISIBLE);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
textView.setVisibility(View.INVISIBLE);
}
}, 4000);
}
}
}
Now, In your Main Activity:
AnimTextView animTextView = new AnimTextView(textView1, textView2, textView3, textView4, ...);
And when you need to start loading:
animTextView.startLoading();
Here is function that will get all TextViews of the Activity(even nested) and hide, you can call it from your Handler:
fun recursiveHideTextAllViews(parent: ViewGroup?) {
for (i in 0 until parent!!.childCount) {
val child: View? = parent.getChildAt(i)
if (child is ViewGroup) {
recursiveHideTextAllViews(child as ViewGroup?)
} else if (child!!.javaClass == AppCompatTextView::class.java) {
child.visibility = View.INVISIBLE;
}
}
}
parent is your Root view
Related
While its in the Document Snapshot loop its adding the ratings to the ratingItemList. I know this for sure because I'm also printing the size of the list in the Log and it's increasing.
But after it comes out of that loop just to be sure I check whether it is empty or not and it prints Empty List in the log.
Can you guys help me out locating the error?
package com.example.ratingapp;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.QuerySnapshot;
import java.util.ArrayList;
import java.util.List;
public class YourRating extends AppCompatActivity {
private List<ratingItem> ratingItemList;
private FirebaseFirestore db;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_your_rating);
ratingItemList=new ArrayList<>();
db = FirebaseFirestore.getInstance();
db.collection("userRatings").get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
if(!queryDocumentSnapshots.isEmpty()){
Log.d("Check1","Ratings Exist");
List<DocumentSnapshot> documentSnapshots = queryDocumentSnapshots.getDocuments();
for(DocumentSnapshot doc : documentSnapshots) {
String rating = doc.getString("Rating");
//Log.d("Rating",rating);
com.google.firebase.Timestamp date = doc.getTimestamp("Date");
//Log.d("Date",date.toString());
ratingItem newRatingItem = new ratingItem(rating, date);
Log.d("Rating", newRatingItem.getRating());
Log.d("Date", newRatingItem.getTimestamp().toString());
ratingItemList.add(newRatingItem);
Log.d("Size ",String.valueOf(ratingItemList.size()));
}
}
else{
Toast.makeText(YourRating.this,"No ratings available!",Toast.LENGTH_LONG).show();
}
}
});
if(ratingItemList.isEmpty()){
Log.d("Empty","Empty List");
}
for(ratingItem r: ratingItemList){
Log.d("Rating1",r.getRating());
Log.d("Date1",r.getTimestamp().toString());
}
}
}
if you call for background thread result in first line and print it on very next line, your callback method does not give guarantee to run before the very next line. It will start to execute thread of first line and without waiting for response run the next line. So you are getting it empty.
Check list size also in callback onSuccess() method, after for loop:
public class YourRating extends AppCompatActivity {
private List<ratingItem> ratingItemList;
private FirebaseFirestore db;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_your_rating);
ratingItemList = new ArrayList<>();
db = FirebaseFirestore.getInstance();
db.collection("userRatings").get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
if (!queryDocumentSnapshots.isEmpty()) {
Log.d("Check1", "Ratings Exist");
List<DocumentSnapshot> documentSnapshots = queryDocumentSnapshots.getDocuments();
for (DocumentSnapshot doc : documentSnapshots) {
String rating = doc.getString("Rating");
//Log.d("Rating",rating);
com.google.firebase.Timestamp date = doc.getTimestamp("Date");
//Log.d("Date",date.toString());
ratingItem newRatingItem = new ratingItem(rating, date);
Log.d("Rating", newRatingItem.getRating());
Log.d("Date", newRatingItem.getTimestamp().toString());
ratingItemList.add(newRatingItem);
Log.d("Size ", String.valueOf(ratingItemList.size()));
}
if (ratingItemList.isEmpty()) {
Log.d("Empty", "Empty List");
}
for (ratingItem r : ratingItemList) {
Log.d("Rating1", r.getRating());
Log.d("Date1", r.getTimestamp().toString());
}
} else {
Toast.makeText(YourRating.this, "No ratings available!", Toast.LENGTH_LONG).show();
}
}
});
}
}
you success listener runs on the background thread and it is a promise that will be run when the data will be obtained from the firebase.
on the other hand the piece of code where you check the array list is empty or not runs on the ui thread. It does not wait for the data to be fetched.
How I'm currently updating my lists in my project clearly isn't the standard for apps. How do would I refresh the list without clearing it?
This code writes just fine, without any interruptions; however, once the user has finished editing, the list is cleared and refreshed, returning the user to the top of the list. What is best practice when it comes to refreshing data, especially if the data is being edited by another user without interrupting the current user.
Writing:
protected void addStep() {
String stepID = Database.push().getKey();
step newStep = new step(recipeID, stepID, "stepImage", "","");
Database.child(stepID).setValue(newStep);
getData();
}
Adapter:
package asdasd.asd;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
/**
* This class is the class responsible for handling the lists of steps that the user will see during the creator phase.
* It works by setting an array adapter which uses the fragment_steps layout displaying it on a list within the StepActivity
* On text listeners are on each of the fields, when the user edits one of the fields, the program waits 600ms, and then uploads the
* data to the database;
* this refreshes the list TODO change the way the list refreshes in the instance of the creator; perhaps add a delay of 1 minuite before refresh, and or if a new step has been added
* A timer is responsible for this delay. The setting of data is to a specific path; being the recipe -> step -> long/shortText field.
*/
public class stepList extends ArrayAdapter<step>{
private Activity context;
private List<step> steps;
private DatabaseReference database;
private Timer timer1,timer2;
public stepList(Activity context, List<step> steps) {
super(context, R.layout.fragment_step, steps);
this.context = context;
this.steps = steps;
}
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//get database
database = FirebaseDatabase.getInstance().getReference("steps");
//init Layout inflater
LayoutInflater inflater = context.getLayoutInflater();
//step
View listViewItem = inflater.inflate(R.layout.fragment_step,null,true);
//step objects
final TextView descriptionText = (TextView) listViewItem.findViewById(R.id.stepRecipeShortText);
final TextView longDescriptionText = (TextView) listViewItem.findViewById(R.id.stepRecipeLongText);
ImageView stepImage = listViewItem.findViewById(R.id.stepImage);
//init step
final step step = steps.get(position);
//get stepID
final String stepID = step.getStepID();
//Set Data
descriptionText.setText(step.getStepDescription());
longDescriptionText.setText(step.getStepLongDescription());
//TODO If user has uploaded an image, then use that, else then use default
//Add listener to descriptionText so that when a user edits the fields, it is uploaded to the same step in the database
descriptionText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// user is typing: reset already started timer (if existing)
if (timer1 != null) {
timer1.cancel();
}
}
#Override
public void afterTextChanged(Editable s) {
timer1 = new Timer();
timer1.schedule(new TimerTask() {
#Override
public void run() {
String newDescriptionText = descriptionText.getText().toString();
addToDatabase(stepID,"stepDescription", newDescriptionText);
}
}, 600);
}
});
//Add listener to LongDescriptionText so that when a user edits the fields, it is uploaded to the same step in the database
longDescriptionText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// user is typing: reset already started timer (if existing)
if (timer2 != null) {
timer2.cancel();
}
}
#Override
public void afterTextChanged(Editable s) {
timer2 = new Timer();
timer2.schedule(new TimerTask() {
#Override
public void run() {
String newLongDescriptionText = longDescriptionText.getText().toString();
addToDatabase(stepID, "stepLongDescription", newLongDescriptionText);
}
}, 600);
}
});
return listViewItem;
}
//Add the data the user is entering to the database; there is a 600ms delay on the period between the user stopping typing and the data being updated.
private void addToDatabase(String id, String location, String text) {
database.child(id).child(location).setValue(text);
}
}
Getting:
public void getData() {
//receives all the recipes and adds them to the list
Database.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Clear the list
listStepsList.clear();
//Iterate through the nodes
for(DataSnapshot stepSnapshot : dataSnapshot.getChildren()){
//get recipe
step step = stepSnapshot.getValue(step.class);
//add step to list, if it is apart of the same recipe.
if(step.getRecipeID().equals(recipeID)) {
listStepsList.add(step);
}
}
//create Adapter
stepList stepAdapter = new stepList(StepActivity.this, listStepsList);
//Attatch adapter to listview
viewStepsList.setAdapter(stepAdapter);
stepAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I assume you're seeing a "big bang"/flash whenever there's a change in the database. If so, that is because you're updating the entire list, even if only a single item in the data was changed.
To improve this, you'll want to more granularly update the adapter for changes. To do so, you can attach a ChildEventListener, which fires events at one level lower in your data structure. So say a new node is added to your list, instead of rebuilding the entire list to add the one node, you'd get a single onChildAdded call with just the one new node. You'd then update listStepsList instead of rebuilding it, and tell the adapter about the changes.
For an example of this, I recommend checking out FirebaseUI, as the adapters in there all use this pattern. They build from a common FirebaseArray class that observes the database, and then have adapter classes to glue to array to the UI. For example, here's how FirebaseRecyclerAdapter connects the changes in the database to minimal updates to the view.
This class extends my main Activity.
public class Numbers extends MainActivity{
public ArrayList<ImageView> getNumbers () {
ArrayList<ImageView> numbers = new ArrayList<>();
ImageView one = (ImageView) findViewById(R.id.one);
numbers.add(one);
return numbers;
}
And I've done some digging but can figure out why my variable "one" is coming back null.
My MainActivity has a ContentView set.
This is the content of my onCreate in MainActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView start = (ImageView) findViewById(R.id.start);
sceneRoot = (ViewGroup) findViewById(R.id.scene_root);
questionView = findViewById(R.id.questionView);
startView = findViewById(R.id.startView);
gameOverView = findViewById(R.id.gameOver);
animSlide = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide);
animSlide.setAnimationListener(this);
animZoom = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.zoom_fade);
animZoom.setAnimationListener(this);
set.addTransition(new Fade())
.addTransition(new Slide(Gravity.RIGHT));
start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getQuestion();
TransitionManager.beginDelayedTransition(sceneRoot, set);
startView.setVisibility(View.GONE);
questionView.setVisibility(View.VISIBLE);
}
});
}
public void getQuestion (){
time = (TextView) findViewById(R.id.timeBar);
time.startAnimation(animSlide);
}
I don't call getNumbers() until after start has been clicked and the animation has started.
public void onAnimationStart(Animation animation){
if(animation == animSlide) {
final Questions questions = new Questions();
Numbers n = new Numbers();
for (int i = 0; i < n.getNumbers().size(); i++) {
n.getNumbers().get(i).setVisibility(View.GONE);
n.getNumbersTen().get(i).setVisibility(View.GONE);
}
n.getNumbers().get(0).setVisibility(View.VISIBLE);
EDIT:
If anyone was wondering, I got it to work by extending the class as a Fragment instead of my MainActivity. Then I just used the fragment in my xml.
Because you extended an Activity class doesn't mean setContentView gets called for that class also. It will only do so if properly started and you call super.onCreate(bundle) from your own implementation of onCreate within Numbers
Basically, you should never new any Activity. It has no life-cycle, and therefore no content view, so findViewById just won't work.
Numbers n = new Numbers();
You could not extend anything and have a data-only class around your list of images.
public class Numbers {
private List<ImageView> numbers = new ArrayList<ImageView>();
public Numbers() {}
public void addNumber(ImageView v) { numbers.add(v); }
public List<ImageView> getNumbers() { return numbers; }
}
And from MainActivity you can find and add as you want.
Number n = new Numbers();
n.addNumber((ImageView) findViewById(R.id.one));
However, I don't know if that is useful, really...
Maybe a Fragment would serve a better purpose if you want a "sub-view" of your Activity, but it's hard to tell.
I am using the following code to display images from drawable folder.
But now i want to display pictures dynamically.Every time a new image is added to the drawable folder i don't want go again in the code and add it in the array it should automatically increment and get displayed.
Any Idea how should I go about this.Just started working on Android.
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.Menu;
import android.widget.ImageView;
import android.app.Service;
import android.os.Handler;
public class MainActivity extends AppCompatActivity {
private static ImageView imgView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imgView = (ImageView) findViewById(R.id.imageView);
final int[] images=images{R.drawable.ic_launcher,
R.drawable.ic_launcher1,R.drawable.ic_launcher2,etc..};
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
int i=0;
public void run() {
imgView.setImageResource(images[i]);
i++;
if(i>images.length-1)
{
i=0;
}
handler.postDelayed(this,5000); //for interval...
}
};
handler.postDelayed(runnable, 5000); //for initial delay..
}
if you want to add the image Dynamically you can Naming the image File like this : Image1.png; Image2.png; and so on.
And then you dont need to call all of them in the array, instead you can using lopp to get the name of the image in resource.
and then get the id using the code below :
public int getID(String resourceName,Context context){
Resources resources = context.getResources();
final int resourceId = resources.getIdentifier(resourceName, "drawable",
context.getPackageName());
return resourceId;
}
Note : after adding the image don't forget to increament the loop. hope it helps.
please keep variable i as static
i.e static int i=0;
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
public void run() {
imgView.setImageResource(images[i]);
i++;
if(i>images.length-1)
{
i=0;
}
handler.postDelayed(this,5000); //for interval...
}
};
handler.postDelayed(runnable, 5000); //for initial delay..
change the handle code as follow
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
public void run() {
imgView.setImageResource(images[i]);
if(i>images.length-1)
{
i=0;
}
else
{
i++;
}
handler.postDelayed(this,5000); //for interval...
}
};
handler.postDelayed(runnable, 5000); //for initial delay..
My slot machine is still in progress. I am trying to get a method from one class to another but I can't figure it out. Could anyone please help me? Here is my first code which I wanted to call the method from the other class:
GameMainActivity:
package com.ics136leeward.slotmachine;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.ViewFlipper;
public class GameMainActivity extends Activity {
ViewFlipper slotOne, slotTwo, slotThree, spinStop;
Button spin, stop, bet;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_main);
this.initSpin();
this.initStop();
}
private void initSpin() { //initialize Spin Method
spin = (Button) findViewById (R.id.spinBtn);
slotOne = (ViewFlipper) findViewById (R.id.slot1);
slotTwo = (ViewFlipper) findViewById (R.id.slot2);
slotThree = (ViewFlipper) findViewById (R.id.slot3);
spinStop = (ViewFlipper) findViewById (R.id.spinstopbutton);
spin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
slotOne.setFlipInterval(40);
slotOne.startFlipping(); //slot 1 spin
slotTwo.setFlipInterval(50);
slotTwo.startFlipping(); //slot 2 spin
slotThree.setFlipInterval(60);
slotThree.startFlipping(); //slot 3 spin
spinStop.showNext(); // shows the stop button
}
});
}
private void initStop() { //initialize Stop Method
stop = (Button) findViewById (R.id.stopBtn);
stop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
slotOne.stopFlipping(); //stops slot 1
slotTwo.stopFlipping(); //stops slot 2
slotThree.stopFlipping(); //stops slot 3
spinStop.showNext(); //shows the spin button again
if(slotOne == slotTwo || slotTwo == slotThree) {
}
}
});
}
}
Here is the second java class which I wanted to call the method getBet1() and getBet5() to the first activity:
Bet:
package com.ics136leeward.slotmachine;
import android.app.Activity;
import android.widget.TextView;
public class Bet extends Activity {
TextView userBet, bankRoll, event;
final int BETONE = 1, BETFIVE = 5;
int uBet = 100, bet;
public void getBet1() {
userBet = (TextView) findViewById (R.id.userBet);
bankRoll = (TextView) findViewById (R.id.bankroll);
uBet -= BETONE;
bet += BETONE;
userBet.setText("Your Bet: " + bet);
bankRoll.setText("" + uBet);
return;
}
public void getBet5() {
userBet = (TextView) findViewById (R.id.userBet);
bankRoll = (TextView) findViewById (R.id.bankroll);
uBet -= BETFIVE;
bet += BETFIVE;
userBet.setText("Your Bet: " + bet);
bankRoll.setText("" + uBet);
return;
}
}
You need to make a Utility class not a Activity class
So change
public class Bet extends Activity {
to
public class Bet // Normal java class
Since its not a Activity class there is not need to initialize views
userBet = (TextView) findViewById (R.id.userBet); //remove them
// Initialize all yours views in Activity
Now in Activity class
Bet bet = new Bet();
int value =bet.getBet1();
In getBet1() do you calculations an return values.
Then in Activity you can set the value to TextView
textView.setText(String.valueOf(value));
Do also check raghav's answer #
Can i Create the object of a activity in other class?
you can define those method static and then call by its class name like
Bet.getBet1();
Bet.getBet5();
or simply you can create class object and then call it
Bet b = new Bet();
b.getBet1()
Note: You don't need to extend to Activity class as you are not using any UI. (Already suggested by Raghunandan)
Try this code in the onCreate method of your calling class i.e. GameMainActivity
Bet bet= new Bet(); // where bet is object of Bet Class
bet.getBet1();
bet.getBet5();
However, You can create the object of Bet class in any method and access the class methods provided their access modifier must be public.