I want to create a menu which "flips" at random time intervals between 5 children of a viewflipper in random order.
I tried the following code and I can get the System.out.println to display my debugging message to be logged in the logcat at random time intervals so that's working.
However, my screen in the emulator is all black.
When I simply use the setDisplayedChild method in the "onCreate" method with a fixed int, it works fine. Can you help me with this? Thanks many!
public class FlipperTest extends Activity {
int randomTime;
int randomChild;
ViewFlipper fliptest;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_beat_the_game);
ViewFlipper fliptest = (ViewFlipper) findViewById(R.id.menuFlipper);
//this would work
//fliptest.setDisplayedChild(3);
while (true){
try {
Thread.sleep(randomTime);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
Random timerMenu = new Random();
randomTime = timerMenu.nextInt(6) * 2000;
Random childMenu = new Random();
randomChild = childMenu.nextInt(5);
fliptest.setDisplayedChild(randomChild);
System.out.println("executes the finally loop");
}
}
}
Don't block the UI thread like you do with Thread.sleep()(+ infinite loop), instead use a Handler, for example, to make your flips:
private ViewFlipper mFliptest;
private Handler mHandler = new Handler();
private Random mRand = new Random();
private Runnable mFlip = new Runnable() {
#Override
public void run() {
mFliptest.setDisplayedChild(mRand.nextInt());
mHandler.postDelayed(this, mRand.nextInt(6) * 2000);
}
}
//in the onCreate method
mFliptest = (ViewFlipper) findViewById(R.id.menuFlipper);
mHandler.postDelayed(mFlip, randomTime);
Here is my final code. I changed a few things: I put the randomTime int in the run method so that it's updated at each run and therefore the handler gets delayed randomly. Otherwise, it will be delayed according to the first run of the randomly generated number and the time intervals will stay the same. I also had to manipulate the generated randomTime int because if the randomly generated int is 0, then the delay is 0 as well and the flip happens instantly which is not what I wanted.
Thanks a million for your help! :-)
private ViewFlipper fliptest;
private Handler testHandler = new Handler();
private Random mRand = new Random();
private Random timerMenu = new Random();
int randomTime;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
fliptest = (ViewFlipper) findViewById(R.id.menuFlipper);
testHandler.postDelayed(mFlip, randomTime);
}
private Runnable mFlip = new Runnable() {
#Override
public void run() {
randomTime = (timerMenu.nextInt(6) + 1) * 2000;
System.out.println("executes the run method " + randomTime);
fliptest.setDisplayedChild(mRand.nextInt(6));
testHandler.postDelayed(this, (mRand.nextInt(6)+ 1) * 2000);
}
};
Related
I have a problem with my simple android app. When I launch it, the questions seems to be a step ahead of the answers I enter as input. For example If the question was to be 3x3 = ?, It will only accept the correct answer for the next question. So any answer I give in the current question will always be wrong. I tested this by continuously entering the same same answer.
Any pointers would be much appreciated. I hope this make sense!
public class MainActivity extends AppCompatActivity {
int value3;
int answer;
EditText answer_field;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageView leftNumber = findViewById(R.id.left_number);
final ImageView rightNumber = findViewById(R.id.right_number);
Button myButton = findViewById(R.id.answer_button);
final int[] numberArray = new int[]{
R.drawable.number_0,
R.drawable.number1,
R.drawable.number2,
R.drawable.number3,
R.drawable.number4,
R.drawable.number5,
R.drawable.number6,
R.drawable.number7,
R.drawable.number8,
R.drawable.number9,
};
leftNumber.setImageResource(numberArray[0]);
rightNumber.setImageResource(numberArray[0]);
myButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Create a random number generator
Random randomNumberGenerator = new Random();
int value1 = randomNumberGenerator.nextInt(10);
leftNumber.setImageResource(numberArray[value1]);
// Create a new random number
int value2 = randomNumberGenerator.nextInt(10);
// Set the right dice image using an image from the diceArray.
rightNumber.setImageResource(numberArray[value2]);
answer_field = findViewById(R.id.answer_field);
answer = valueOf(answer_field.getText().toString());
value3 = value1 * value2;
if(value3 == answer) {
showToast("Correct");
}
}
});
}
public void showToast(String text){
Toast.makeText(MainActivity.this, text, Toast.LENGTH_LONG).show();
}
}
Thank you!
You are generating the values at the onClick of your answer button wich means that the values to multiply will be set (or reset) randomly every time the user hit the answer button; put the logic to generate the values outside the onclick, so when the user clicks on it only the comparison will be made, somewhat like this:
[...]
leftNumber.setImageResource(numberArray[0]);
rightNumber.setImageResource(numberArray[0]);
// Create a random number generator
Random randomNumberGenerator = new Random();
int value1 = randomNumberGenerator.nextInt(10);
leftNumber.setImageResource(numberArray[value1]);
// Create a new random number
int value2 = randomNumberGenerator.nextInt(10);
// Set the right dice image using an image from the diceArray.
rightNumber.setImageResource(numberArray[value2]);
answer_field = findViewById(R.id.answer_field);
myButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
answer = valueOf(answer_field.getText().toString());
value3 = value1 * value2;
if(value3 == answer) {
showToast("Correct");
}
}
});
[...]
I'm having some trouble with an issue in my Android Studio application that is to do with pressing the back button. Basically the user inputs into a text view and another method (not included as it works fine but I can post if requested) searches an API for the relevant data and displays it in a recycler view. Then the user makes a selection from the recycler view and the method below parses a JSON file and adds relevant data from it to an array in a separate class "Director". Once this happens the next activity "GraphActivity" starts and draws a number of nodes on a canvas for each director in the array.
The issue I am having is that when I make an input into the text view and then press the back button to close the soft keyboard on my Android device (it covers up the recycler view) and go through the steps I just mentioned, when the GraphActivity activity starts it does not work (no nodes are drawn). However, if I then press the back button to go back to the first activity and then make the same selection from the recycler view again, it works.
From what I can tell, the issue seems to be with the data being added to the "Director" array but I cannot figure out what is causing it, Logcat shows that the first time the GraphActivity activity starts the "Director" array is empty and therefore, no nodes are drawn but when I press back and then make the same selection again the array has the correct data and the nodes are drawn.
I would really appreciate some help with this as I am very new to Android Studio and have been searching around for hours to try and find a solution.
public class MainActivity extends AppCompatActivity implements MainAdapter.OnCompanyClickedListener {
private RecyclerView recyclerView;
private TextView userInput;
private Button search;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.rView);
RecyclerView.LayoutManager manager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(manager);
}
//Parse JSON File
public void searchCompanyDirectors(final String companyNumber){
Thread thread = new Thread(new Runnable(){
#Override
public void run(){
HttpClient httpClient = new HttpClient();
try{
final String response = httpClient.run("myURL");
runOnUiThread(new Runnable(){
#Override
public void run(){
try {
JSONObject jsonObject = new JSONObject(response);
JSONArray array = jsonObject.getJSONArray("items");
for (int i = 0; i < array.length();i++){
JSONObject o = array.getJSONObject(i);
String appointment = o.getString("links");
String parsedAppointment = parseApp(appointment);
Director director = new Director(o.getString("name"),parsedAppointment);
Director.directorList.add(director);
}
}catch (JSONException e){
e.printStackTrace();
}
}
});
}
catch (Exception ex){
ex.printStackTrace();
}
}
});
thread.start();
}
#Override
public void onCompanyClicked(int position) {
Toast.makeText(this,Company.companyList.get(position).getTitle(),Toast.LENGTH_LONG).show();
chosenCompanyNumber = Company.companyList.get(position).getCompanyNumber();
chosenCompany = Company.companyList.get(position).getTitle();
searchCompanyDirectors(chosenCompanyNumber);
Intent intent = new Intent(this,GraphActivity.class);
startActivity(intent);
}
}
The GraphActivity class:
public class GraphActivity extends AppCompatActivity {
DrawGraph drawGraph;
#SuppressLint("ClickableViewAccessibility")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
drawGraph = new DrawGraph(this);
setContentView(drawGraph);
The DrawGraph class:
public class DrawGraph extends View {
private Paint p1;
private Paint p2;
private Paint p3;
private Paint paint;
private Path path;
private Point point1;
private int radius = 300;
double x;
double y;
double angle;
int mWidth = this.getResources().getDisplayMetrics().widthPixels;
int mHeight = this.getResources().getDisplayMetrics().heightPixels;
List<Director> directorList = Director.getDirectorList();
private String chosenCompany = MainActivity.getChosenCompany();
private static List<Float> ordinates = new ArrayList<>();
public DrawGraph(Context context) {
super(context);
p1 = new Paint(Paint.ANTI_ALIAS_FLAG);
p1.setStrokeWidth(10);
p2 = new Paint(Paint.ANTI_ALIAS_FLAG);
p2.setStrokeWidth(10);
p3 = new Paint(Paint.ANTI_ALIAS_FLAG);
p3.setStrokeWidth(5);
path = new Path();
paint = new Paint();
point1 = new Point(mWidth/2, mHeight/2);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Colors
p1.setStyle(Paint.Style.FILL);
p1.setColor(Color.RED);
p2.setStyle(Paint.Style.FILL);
p2.setColor(Color.GREEN);
p3.setStyle(Paint.Style.STROKE);
p3.setColor(Color.CYAN);
paint.setColor(Color.BLACK);
paint.setTextSize(18f);
paint.setAntiAlias(true);
paint.setTextAlign(Paint.Align.CENTER);
Rect bounds = new Rect();
paint.getTextBounds(chosenCompany, 0, chosenCompany.length(),bounds);
//Draw company and directors
for (int i = 1; i <= directorList.size(); i++)
{
//Divide the company node
angle = i * (360/directorList.size());
x = point1.x + radius * cos(angle);
y = point1.y + radius * sin(angle);
//Draw directors
canvas.drawCircle((float)x,(float)y - (bounds.height() / 2), bounds.width() + 5,p2);
canvas.drawText(directorList.get(i-1).getName(),(float)x,(float)y,paint);
}
Okay, I've found an issue in DrawGraph class noted here;
List<Director> directorList = Director.getDirectorList();
private String chosenCompany = MainActivity.getChosenCompany();
You should not directly access the data objects of one activity from another. The previous activity could be destroyed etc as per Android OS and its lifecycle. You must think of it like out of sight, out of memory; which can cause the current activity to not be able to access the data in the previous one.
You'd need to properly send the parsed data from your JSON file, the variables List directorList and the String chosenCompany from your MainActivity to GraphActivity.
You can send data from one activity to another by adding it to the intent with which you call the next activity. The Intent class has features to help you send a small amount of information via itself; called putExtra() and getExtra(). More about them here.
You will need to add those variable datasets through intent.putExtra and fetch them back in your other activity onCreate by using getExtras.
Activity A >> Intent call to Activity B + Data >> Activity B
Hope this helps.
I'm an Android newbie developer and having an issue with a tiny Android app I'm developing which I suspect is related to timing of dynamic view creation. It's a little scorekeeper app and it has dynamically generated player buttons and text fields, defined in a Player class. Here's an excerpt of how I'm doing this:
public Player(String name, int score, int number, boolean enabled, RelativeLayout mainScreen, List<Button> presetButtonList, Editor editor, Context context) {
super();
this.name = name;
this.score = score;
this.number = number;
this.enabled = enabled;
this.editor = editor;
// Get the lowest child on the mainScreen
Integer count = mainScreen.getChildCount(), lowestChildId = null;
Float lowestChildBottom = null;
for (int i = 0; i < count; i++) {
View child = mainScreen.getChildAt(i);
if ((lowestChildId == null || child.getY() > lowestChildBottom) &&
!presetButtonList.contains(child)) {
lowestChildId = child.getId();
lowestChildBottom = child.getY();
}
}
playerNameText = (EditText) setTextViewDefaults(new EditText(context), name);
playerNameText.setSingleLine();
// playerNameText.setMaxWidth(mainScreen.getWidth());
// playerNameText.setEllipsize(TextUtils.TruncateAt.END); //TODO: Prevent names which are too long for screen
playerNameText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable changedText) {
setName(changedText.toString());
updateScreen();
}
public void beforeTextChanged(CharSequence changedText, int start, int count, int after) {}
public void onTextChanged(CharSequence changedText, int start, int before, int count) {}
});
RLParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if (lowestChildId != null) {
RLParams.addRule(RelativeLayout.BELOW, lowestChildId);
} else {
RLParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
RLParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
}
RLParams.setMargins(0, 10, 0, 10);
mainScreen.addView(playerNameText, RLParams);
the class further defines buttons and etc in a similar fashion, aligning tops with the name text view. I call this when a user clicks a button to add a player, and it works fine, displaying each player below the first on down the screen. The problem comes in when I'm loading a bunch of saved players at the start. Here's where i load the players:
public void loadData() {
RelativeLayout mainScreen = (RelativeLayout)findViewById(R.id.relativeLayout);
playerCount = savedData.getInt("playerCount", playerCount);
Player player;
String name;
playerList.clear();
for (int i=1; i<=playerCount; i++) {
name = savedData.getString("name" + i, null);
if (name != null) {
Log.v("name", name);
player = new Player(name, savedData.getInt("score" + i, 0), i, savedData.getBoolean("enabled" + i, false), mainScreen, presetButtonList, editor, this);
playerList.add(player);
player.updateScreen();
}
}
updateScreen();
}
Finally, I call the loadData() method when the app starts, here:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
savedData = this.getPreferences(Context.MODE_PRIVATE);
editor = savedData.edit();
presetButtonList.add((Button)findViewById(R.id.newGame));
presetButtonList.add((Button)findViewById(R.id.addPlayer));
presetButtonList.add((Button)findViewById(R.id.removePlayer));
loadData();
}
The result? When there are more than two players to load, all the players get loaded to the same spot, on top of Player 2.
I suspect somehow that the players are all being generated at the same time and thus all believing that the lowest player view is Player 1, and not checking each other. I've tried triggering the load later than onCreate() and it still happens. I also tried adding a 3 second sleep within the for loop of loadData() after each player loads to see if that helped, but no luck.
Am I making bad assumptions? What am I doing wrong and how might I fix it?
I suspect what is happening here is that you are attempting to position views that don't have IDs (it looks like you don't set them anywhere in your code). Dynamically created views don't automatically have IDs, so a call to getId() will return NO_ID (See documentation). If you want to use an ID you will have to set it yourself using View.setId().
It was a timing issue after all. I needed to wait for the main screen to load before I could add views to it. Unfortunately, it would never load until all activity on the UI thread was finished.
Thus, I needed threading. I ended up putting loadData() in a thread, like this:
new Thread(new loadDataAsync(this)).start();
class loadDataAsync implements Runnable {
MainActivity mainActivity;
public loadDataAsync(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
#Override
public void run() {
try {
Thread.sleep(700);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
loadData(mainActivity);
}
}
Then I created the players back on the UI thread after the initial sleep to give time for the UI to load.
runOnUiThread(new Runnable() {
Player player;
RelativeLayout mainScreen = (RelativeLayout)findViewById(R.id.relativeLayout);
#Override
public void run() {
player = new Player(savedData.getString("name" + playerNum, null), savedData.getInt("score" + playerNum, 0), playerNum, savedData.getBoolean("enabled" + playerNum, false), mainScreen, presetButtonList, editor, mainActivity);
playerList.add(player);
player.updateScreen();
mainScreen.invalidate();
}
});
Not the most elegant solution, but it works for now.
I can't for the life of me get this do while loop running on a button click even with a runnable and handler. Can anyone please show me where I have made my mistake?
{//Class start
Button roll;
int numberOfDice;
int diceCounter;
EditText diceBox;
EditText outputBox;
String diceNum;
Handler myHandler = new Handler();
private boolean Running;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_roller_screen);
roll = (Button) findViewById(R.id.roll_Button);
diceBox = (EditText) findViewById(R.id.numberOfDiceBox);
outputBox = (EditText) findViewById(R.id.outputBox);
roll.setOnClickListener(new View.OnClickListener() {
public void onClick(View v)
{
myHandler.post(runner);
}
});
}
Runnable runner = new Runnable() {
int dicecounter = 1;
Random rand = new Random();
public void run() {
do{
int dice_Value = rand.nextInt((6 - 1) + 1) - 1;
diceNum = diceBox.getText().toString();
numberOfDice = Integer.parseInt(diceNum);
outputBox.setText("Dice"+dicecounter+dice_Value);
dicecounter++;
}while(diceCounter <= numberOfDice);
myHandler.post(this);
}
};
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.roller_screen, menu);
return true;
}
}
I can't figure out if it's my logic or if I'm using runners and handlers completely wrong.
This is almost certainly an endless loop problem: dicecounter starts at one so if you enter a value less than two into diceBox the do..while loop will never terminate. The counter should start at zero and you should validate the number of dice value entered by the user (including checking for number format exception, unless the edit control only allows digits?)
But in any case, why is there a do..while loop there anyway? All it does is overwrite the text in outputBox, so even if the loop was fixed all the user will see is the result of the last iteration of the loop.
Background: I have a IOIO which I am using to measure the output from an photodiode, this is then converted into a digital output. I need to find the frequency at which the signal changes between 1 and 0. Someone gave me some code that I could use to measure the frequency, but I have no idea how to integrate it into my existing app. I know that the way I have implemented it will not work because the UI thread which updates the variable is waiting on the return from the other thread that calculates the frequency, so that thread will only get the value of diode when it starts running. So how do I make the frequency thread have a real-time value for diode and also after it has calculated the frequency pass it back to the UI thread to be displayed?
Here is my UI thread (FrequencyApp.java):
public class FrequencyApp extends IOIOActivity {
private TextView textView_;
private TextView textView2_;
private TextView textView3_;
private ToggleButton toggleButton_;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textView_ = (TextView)findViewById(R.id.TextView);
textView2_ = (TextView)findViewById(R.id.TextView2);
textView3_ = (TextView)findViewById(R.id.FrequencyLabel);
toggleButton_ = (ToggleButton)findViewById(R.id.ToggleButton);
enableUi(false);
}
class Looper extends BaseIOIOLooper {
private AnalogInput input_;
private DigitalOutput led_;
volatile int diode;
private long frequency;
#Override
public void setup() throws ConnectionLostException {
try {
input_ = ioio_.openAnalogInput(31);
led_ = ioio_.openDigitalOutput(IOIO.LED_PIN, true);
enableUi(true);
} catch (ConnectionLostException e) {
enableUi(false);
throw e;
}
}
#Override
public void loop() throws ConnectionLostException {
try {
led_.write(!toggleButton_.isChecked());
float reading = input_.getVoltage();
if(reading > 1){
diode = 1;
} else {
diode = 0;
}
if(toggleButton_.isChecked()){
FrequencyThread frequencyTaskThread = new FrequencyThread();
frequencyTaskThread.setPriority(Thread.NORM_PRIORITY-1); //Make the background thread low priority. This way it will not affect the UI performance
frequencyTaskThread.start();
frequency = (long) frequencyTaskThread.run(diode);
frequencyTaskThread.stop();
}
setText(Float.toString(reading), Long.toString(diode), Long.toString(frequency));
Thread.sleep(100);
} catch (InterruptedException e) {
ioio_.disconnect();
} catch (ConnectionLostException e) {
enableUi(false);
throw e;
}
}
}
#Override
protected IOIOLooper createIOIOLooper() {
return new Looper();
}
private void enableUi(final boolean enable) {
runOnUiThread(new Runnable() {
#Override
public void run() {
toggleButton_.setEnabled(enable);
}
});
}
private void setText(final String str,final String str2,final String str3 ) {
runOnUiThread(new Runnable() {
#Override
public void run() {
textView_.setText(str);
textView2_.setText(str2);
textView3_.setText(str3);
}
});
}
}
Here is the thread for calculating the frequency(FrequencyThread.java:
public class FrequencyThread extends Thread {
public float run(int diode){
// Find frequency to the nearest hz (+/- 10%)
// It's assumed that some other process is responsible for updating the "diode"
// variable. "diode" must be declared volatile.
long duration = 1000; // 1 second
final int interval = 100; // sampling interval = .01 second
int oldState = diode;
int count = 0;
final long startTime = System.currentTimeMillis();
final long endtime = startTime + duration;
while (System.currentTimeMillis() < endtime) {
// count all transitions, both leading and trailing
if (diode != oldState) {
++count;
oldState = diode;
}
Thread.sleep(interval);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// find the actual duration
duration = System.currentTimeMillis() - startTime;
// Compute frequency. The 0.5 term is because we were counting both leading and
// trailing edges.
float frequency = (float) (0.5 * count / (duration/1000));
return frequency;
}
}
The way you have it set up easiest would probably be to use a Handler to send messages from the FrequencyThread to the main thread. The way that is probably preferred to use an AsyncTask to abstract away the Thread / Handler and make the whole situation a bit easier to deal with. Doing so might require you to put the IOIOLooper thing into an AsyncTask though, I have no experience with that board or its Java API.
Also you shouldn't need the "runOnUiThread", or the Runnable at all in your setText() method. That seems to be only being called from the main thread anyway.
What you'll want to do is override your Handlers handleMessage() to call setText(). Then inside the FrequencyThread make a call to handler.sendMessage() passing back the data and / or message(i.e. error).
I tried using the code you've posted to make an example but I am having trouble following it honestly.