How to randomly set text to buttons from SQLite without repetition? - java

I have a db imported to assets, and i read from it and randomly set text to buttons and a texview, with code below:
mDbHelper.open();
Cursor c = mDbHelper.getTestData();
List<Answer> labels = new ArrayList<Answer>();
labels.add(new Answer(c.getString(2), true));
labels.add(new Answer(c.getString(3), false));
labels.add(new Answer(c.getString(4), false));
labels.add(new Answer(c.getString(5), false));
Collections.shuffle(labels);
question.setText(c.getString(1));
bOdgovor1.setText(labels.get(0).option);
bOdgovor1.setTag(labels.get(0));
bOdgovor1.setOnClickListener(clickListener);
bOdgovor2.setText(labels.get(1).option);
bOdgovor2.setTag(labels.get(1));
bOdgovor2.setOnClickListener(clickListener);
bOdgovor3.setText(labels.get(2).option);
bOdgovor3.setTag(labels.get(2));
bOdgovor3.setOnClickListener(clickListener);
bOdgovor4.setText(labels.get(3).option);
bOdgovor4.setTag(labels.get(3));
bOdgovor4.setOnClickListener(clickListener);
Here's my TestAdapter code for db:
public Cursor getTestData()
{;
try
{
String sql ="SELECT * FROM tblPitanja ORDER BY RANDOM() LIMIT 1";
Cursor mCur = mDb.rawQuery(sql, null);
if (mCur!=null)
{
mCur.moveToNext();
}
return mCur;
}
catch (SQLException mSQLException)
{
Log.e(TAG, "getTestData >>"+ mSQLException.toString());
throw mSQLException;
}
}
It works perfectly when it comes to setting questions to buttons, but questions repeat. How to avoid that?

there are more approaches to solve your problem:
execute the sql-statement (without limiting) at the beginning and move to the next entry of the cursor when a quesion is answered correctly
buffer the questions which where already answered
the second approach could be done as follows:
first, change your method and sql, including a where-clause:
public Cursor getTestData(String whereClause)
{;
try
{
String sql ="SELECT * FROM tblPitanja WHERE 1 = 1 " + whereClause + " ORDER BY RANDOM() LIMIT 1";
[...]
second, buffer the already answered questions in your game-class:
add a LinkedList to your game-class
LinkedList<Long> mAnsweredQuestions = new LinkedList<Long>();
add already answered questions to the LinkedList:
Cursor c = mDbHelper.getTestData(generateWhereClause());
mAnsweredQuestions.add(c.getLong(0));
List<Answer> labels = new ArrayList<Answer>();
[...]
add a function which generates the where-clause:
private String generateWhereClause(){
StringBuilder result = new StringBuilder();
for (Long l : mAnsweredQuestions){
result.append(" AND " + YOURID + " <> " + l);
}
return result.toString();
}

You could just save the Cursor objects into an ArrayList and then use contains to find out if that question was alredy asked. (Note: contains useses the equals method)
public class YourClass {
java.util.ArrayList<Long> cursorList = new java.util.ArrayList<Long>();
public void YourMethod {
Cursor c = mDbHelper.getTestData();
long l = c.getLong(0);
while(cursorList.contains(l))
{
Cursor c = mDbHelper.getTestData();
l = c.getLong(0);
}
cursorList.add(l);
}
}

The solution depends on your application logic. One of possible solutions is to remove "LIMIT 1" from your query and load all questions randomly sorted.
If your app does something like "show me next random question I have never seen before", you have to keep the list of already visited questions (in memory, or in DB - again depends on your application logic. For DB solution is as simple as add visited column to your database.

For this problem, Better you maintain the list of question(object or id) your asked. Before display any question to the user check the current question is in your array list or not. If current qus is in arraylist then you can call getTestData(), otherwise you can display the question and add id or qus object to the array list.

Related

Can't fetch data from SQLite database in AS

I'm creating a Quiz app in Android Studio, and so I created a Database that contains the questions. In the helper class I have a method getAllQuestions that transfers the questions from the database to an array list, so then I can extract the Questions in Main and setText in Buttons. However, while I run my app the buttons and the Question are empty, without the app throwing exceptions like nullpointer if the list itself was empty.
DB_Helper methods:
private void addQuestions(QuestionsDataBase Questions)
{
ContentValues cv = new ContentValues();
cv.put(QuestionTable.Column_Question,Questions.getQuestion());
cv.put(QuestionTable.Column_Option1,Questions.getOption1());
cv.put(QuestionTable.Column_Option2,Questions.getOption2());
cv.put(QuestionTable.Column_Option3,Questions.getOption3());
cv.put(QuestionTable.Column_Option4,Questions.getOption4());
cv.put(QuestionTable.Column_Correct_Ans,Questions.getQuestion());
db.insert(QuestionTable.Table_Name,null,cv);
}
public ArrayList getAllQuestions(){
ArrayList<QuestionsDataBase> questionsList = new ArrayList<>();
db = getReadableDatabase();
String[] Projection ={
QuestionTable._ID,
QuestionTable.Column_Question,
QuestionTable.Column_Option1,
QuestionTable.Column_Option2,
QuestionTable.Column_Option3,
QuestionTable.Column_Option4,
QuestionTable.Column_Correct_Ans
};
Cursor c = db.query(QuestionTable.Table_Name,
Projection,
null,
null,
null,
null,
null);
if(c.moveToFirst()){
do{
QuestionsDataBase questions = new QuestionsDataBase();
questions.setQuestion(c.getString(c.getColumnIndexOrThrow(QuestionTable.Column_Question)));
questions.setOption1(c.getString(c.getColumnIndexOrThrow(QuestionTable.Column_Option1)));
questions.setOption2(c.getString(c.getColumnIndexOrThrow(QuestionTable.Column_Option2)));
questions.setOption3(c.getString(c.getColumnIndexOrThrow(QuestionTable.Column_Option3)));
questions.setOption4(c.getString(c.getColumnIndexOrThrow(QuestionTable.Column_Option4)));
questions.setCorrectAns(c.getInt(c.getColumnIndexOrThrow(QuestionTable.Column_Correct_Ans)));
questionsList.add(questions);
questionsList.add(questions);
}while(c.moveToNext());
}
c.close();
return questionsList;
}
Methods in MainActivity:
private void fecthDB(){
DB_Helper db = new DB_Helper(this);
questionList = db.getAllQuestions();
startQuiz();
}
private void startQuiz()
{
questionTotalCount = questionList.size(); // the total amount of questions in the current quizactivity( is set to 10)
Collections.shuffle(questionList); // shuffle the questions form the database that are stored in QuestionList
showQuestions();
nextbutton.setOnClickListener(view -> {
if(!answered){
if(option1.isChecked() || option2.isChecked() || option3.isChecked() || option4.isChecked())
QuizOperations();
}
});
}
private void showQuestions() // Showing the question and the options from database
{
rbGroup.clearCheck();
if(questionCounter< questionTotalCount) // if not all the questions yet answered set text to new questions
{
currentQuestions = questionList.get(questionCounter);
questionCount.setText(currentQuestions.getQuestion());
option1.setText(currentQuestions.getOption1());
option2.setText(currentQuestions.getOption2());
option3.setText(currentQuestions.getOption3());
option4.setText(currentQuestions.getOption4());
questionCounter ++; // +1 to question counter
answered = false; // tye quiz is not yet completely answered while the current question is smaller then total
nextbutton.setText("Next");
questionCount.setText("Questions: " + questionCounter + "/" + questionTotalCount); // how many questions answered out of 10
}
else{// if all the questions are answered
handler.postDelayed(() -> {
Intent intent = new Intent(getApplicationContext(),QuizActivity.class); // open the next activity
}, 500); // wait for a half second
}
}
I believe you want to set other fields on your QuestionsDataBase object here rather than setQuestion() for each column in the database query:
questions.setQuestion(c.getString(c.getColumnIndexOrThrow(QuestionTable.Column_Option1)));
questions.setQuestion(c.getString(c.getColumnIndexOrThrow(QuestionTable.Column_Option2)));
questions.setQuestion(c.getString(c.getColumnIndexOrThrow(QuestionTable.Column_Option3)));
questions.setQuestion(c.getString(c.getColumnIndexOrThrow(QuestionTable.Column_Option4)));
questions.setQuestion(c.getString(c.getColumnIndexOrThrow(QuestionTable.Column_Correct_Ans)));
Thanks, edited it. Now it shows the options, but doesn't show the question itself.
You are setting text to questionCount twice where the later overwrites the first one. Maybe the first one should set the question textview instead.

Android Time Ranking

I am currently having a problem with the time ranking on my android application. I am working with the sqlite database that will store the time the user finished solving the problem. I will use that sqlite database later to display the rankings of the user that played the game.
For example, the player finished the puzzle for about 1:25 (1 minute and 25 seconds), the app will store 1:25 on the sqlite database. But I'm having a problem on doing that.
I can store it on the sqlite as a string but I can't use ORDER
BY.
I tried storing it as an int but output says: invalid int
I tried storing it as a string while removing the colon (:) but the database on returns "125".
My question is: What is the best way to store that specific time value on sqlite databases?
I read this http://www.sqlite.org/lang_datefunc.html documentation about the Time and Date Functions but it seems it can't be applied on custom time. It only applies on either the date now or current time. Please I need help on this. I'm an android and sqlite amateur. This project is my thesis for this sem.
Any comments and suggestions are accepted. Thanks in advance!
By the way, this is the code that retrieves all the data on my sqlite. It's a testing program, to test if my codes/to modify codes and see if they are working.
try {
String selectQuery = "SELECT * FROM " + DatabaseHelper.TABLE_OUTLET + " WHERE category LIKE '" + cat + "' ORDER BY category ASC";
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
// Read columns data
int outlet_id = cursor.getInt(cursor.getColumnIndex("outlet_id"));
String outlet_name = cursor.getString(cursor.getColumnIndex("outlet_name"));
String outlet_type = cursor.getString(cursor.getColumnIndex("outlet_type"));
String category = cursor.getString(cursor.getColumnIndex("category"));
// dara rows
TableRow row = new TableRow(context);
row.setLayoutParams(new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
String[] colText = {outlet_name, outlet_type, category};
for (String text : colText) {
TextView tv = new TextView(this);
tv.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT));
tv.setGravity(Gravity.CENTER);
tv.setTextSize(16);
tv.setPadding(5, 5, 5, 5);
tv.setText(text);
row.addView(tv);
}
tableLayout.addView(row);
}
}
db.setTransactionSuccessful();
} catch (SQLiteException e) {
e.printStackTrace();
} finally {
db.endTransaction();
// End the transaction.
db.close();
// Close database
}
}
1:25 convert this to seconds only.
That is 1 * 60 + 25 = 85. So store 85.
Every time you want to store it do this. And you can do the reverse on restoring.
In example,
sec = 85 % 60 = 25
min = 85 / 60 = 1
You will get 1:25
Following the suggestion of #ShreyashSSarnayak I managed to fix this problem.
This is the resulting code:
try {
String selectQuery = "SELECT * FROM " + DatabaseHelper.TABLE_OUTLET + " WHERE category LIKE '" + cat + "' ORDER BY outlet_type ASC";
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
// Read columns data
int outlet_id = cursor.getInt(cursor.getColumnIndex("outlet_id"));
String outlet_name = cursor.getString(cursor.getColumnIndex("outlet_name"));
int outlet_type = Integer.parseInt(cursor.getString(cursor.getColumnIndex("outlet_type")));
String category = cursor.getString(cursor.getColumnIndex("category"));
// dara rows
TableRow row = new TableRow(context);
row.setLayoutParams(new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
int m, s;
s = outlet_type % 60;
m = outlet_type / 60;
String[] colText = {outlet_name, String.format("%02d:%02d", m, s), category};
for (String text : colText) {
TextView tv = new TextView(this);
tv.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT));
tv.setGravity(Gravity.CENTER);
tv.setTextSize(16);
tv.setPadding(5, 5, 5, 5);
tv.setText(text);
row.addView(tv);
}
tableLayout.addView(row);
}
}
db.setTransactionSuccessful();
} catch (SQLiteException e) {
e.printStackTrace();
} finally {
db.endTransaction();
// End the transaction.
db.close();
// Close database
}
}
I'll suggest you to convert your time to seconds and store it as integer.
Use INTEGER as database type instead. Check here available types. Then you need to convert the INTEGER value to time format using standard java clases or any other if you like.
Using INTEGER you will be also able to sort them easily.

Removing country code from phone number

I am stuck with a problem in which some contacts contain a country code while others don't.
I have code for fetching contact numbers which are being saved in list. The list looks like this:
[9999999999, 91-8888888888, 91-0000000000, 1111111111]
Now, I want to remove country code from those numbers which have it. I can't figure out a way to check this.
Here is my code for fetching contacts:
ContentResolver cr = getApplicationContext().getContentResolver(); //Activity/Application android.content.Context
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, "UPPER(" + ContactsContract.Contacts.DISPLAY_NAME + ") ASC");
if(cursor.moveToFirst())
{
do
{
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
if(Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0)
{
Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",new String[]{ id }, null);
while (pCur.moveToNext())
{
String contactName = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY));
String contactNumber = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
allContacts.add(contactName);
contactNumber = contactNumber.replaceAll("\\s+","");
allnumbers.add(contactNumber);
break;
}
pCur.close();
}
} while (cursor.moveToNext()) ;
}
}
Here, allnumbers contains the number (where some have country code with them and others don't). How can I remove the country code from those who have it?
Any help or keywords to search will be appreciated, thanks.
If you want to remove the first occurance of a string containing '91-', you could simply use the replaceFirst() method. So your code will end up like this:
contactNumber.replaceAll("\\s+","").replaceFirst("91-","");
If you want to check if a string starts with '91-', you could use this:
if (contactNumber.startsWith("91-")) {
}
In your sample with array of numbers you have dash(-) between countrycode. So you can just use indexOf to determine if number has country code. You can do the same thing using regexp([\d]+-[\d]+)
Also you can count degits. Usually numbers without country codes have only 10 digits. It depends on your situation
Just try
contactNumber = contactNumber.replace("+91", "");
Hope this helps...

My cursor gets only last entry from sql database, but I need all of them

every one. I'm trying to get data from database, but it shows me only the last entry. The same code works for other entries, but for long it doesn't. I have searched a lot and always answer is teh same I have, so I don't know what to do. My code:
hepublic void loadRevenueDate() {
String dateFormat = "yyyy/MM/dd";
SimpleDateFormat formater = new SimpleDateFormat(dateFormat);
long l=0;
Calendar cal = Calendar.getInstance();
// Open database
db.open();
Cursor c = db.getRevData();
if (c.moveToFirst()) {
do {
l = c.getLong(3);
} while (c.moveToNext());
}
cal.setTimeInMillis(l);
String date = formater.format(cal.getTime());
revDate.setText(date);
// Close the cursor
c.close();
c = null;
// And close the database
db.close();
}
Please help me. Thanks in advance.
The cursor correctly reads over all the records - but you overwrite and ignore previous values when re-assigning to the l variable.
Do the thing "per row" inside the loop or otherwise save the relevant data into a collection such as an ArrayList. I imagine your UI needs to be redesigned, but this won't "skip" any values:
List<String> dates = new ArrayList<String>();
if (c.moveToFirst()) {
do {
// Stuff that happens for EACH record
long l = c.getLong(3);
cal.setTimeInMillis(l);
String date = formater.format(cal.getTime());
// .. and actually save the data for use later
dates.add(date)
} while (c.moveToNext());
}
// To make the text pretty (e.g. without surrounding braces),
// search for "java list join" or otherwise consume the List in the UI
// in a meaningful fashion.
revDate.setText("" + dates);
From the code it looks like you're just iterating over the values until you get to the last one. If you want to do something with all the values, you need to add something into the loop:
public void loadRevenueDate() {
String dateFormat = "yyyy/MM/dd";
SimpleDateFormat formater = new SimpleDateFormat(dateFormat);
long l=0;
Calendar cal = Calendar.getInstance();
// Open database
db.open();
Cursor c = db.getRevData();
long accum = 0;
if (c.moveToFirst()) {
do {
l = c.getLong(3);
// Do something here. For example:
accum += l;
} while (c.moveToNext());
}
cal.setTimeInMillis(l);
String date = formater.format(cal.getTime());
revDate.setText(date);
// Close the cursor
c.close();
c = null;
// And close the database
db.close();
}
The issue here is that you loop through all the result rows, assigning the long value from each row to 'l' over and over (overwriting the previous value), before you do anything with 'l'. So by the time you come to use 'l' (once the looping has finished), it will only contain the value from the final row.
If you wish to do something with each value you assign to 'l', you need to do it within the do...while loop. I.e.
if (c.moveToFirst()) {
do {
l = c.getLong(3);
//Do the actual thing with this row's 'l' here
//E.g.
System.out.println("l is currently set to: " + l);
} while (c.moveToNext());
}
Hope that makes sense.

Calling a Method from button click

I'm new to java and I'm trying to run a method once a button is clicked and I am unsure if I'm on the right track. I am selecting 10 questions from a Sqlite database and would like to cycle through each question 0-9 everytime the button is clicked. Once all 10 questions are asked I will move to another activity. I have the app displaying the first question fine but I am having trouble calling the showQuestions method and increasing the questionNum int by one when the button is clicked and passing this onto the method. Any help would be appreciated!
This is the showQuestions method which I am trying to call.
public void showQuestions(Cursor cursor) {
cursor.moveToPosition(questionNum);
while (cursor.moveToNext()) {
// Collect String Values from Query
StringBuilder questiontxt = new StringBuilder("");
StringBuilder answertxt1 = new StringBuilder("");
StringBuilder answertxt2 = new StringBuilder("");
StringBuilder answertxt3 = new StringBuilder("");
StringBuilder answertxt4 = new StringBuilder("");
String question = cursor.getString(2);
String answer = cursor.getString(3);
String option1 = cursor.getString(4);
String option2 = cursor.getString(5);
String option3 = cursor.getString(6);
questiontxt.append(question).append("");
answertxt1.append(answer).append("");
answertxt2.append(option1).append("");
answertxt3.append(option2).append("");
answertxt4.append(option3).append("");
}
}
This is the code for the button which I am working on. There are 4 buttons.
public void onClick(View v) {
switch (v.getId()) {
case R.id.option1_button:
if (questionNum<9) {
questionNum ++;
}
Questions.this.showQuestions(null);
break;
}
And this is the XML code for the buttons.
<Button
android:id="#+id/option1_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/option3_button"/>
OnClick Listener for the button
View option1_button = findViewById(R.id.option1_button);
option1_button.setOnClickListener(this);
Database Query and Allocation to Cursor
//Get the questions and allocate them to the Cursor
public Cursor getQuestions() {
//Select Query
String loadQuestions = "SELECT * FROM questionlist ORDER BY QID LIMIT 10";
SQLiteDatabase db = questions.getReadableDatabase();
Cursor cursor = db.rawQuery(loadQuestions, null);
startManagingCursor(cursor);
return cursor;
}
Thanks in advance.
Questions.this.showQuestions(null);
Why you pass null to the showQuestions method?
Let me guess - it crashes with NullPointerException.
Did you tried
showQuestions(getQuestions())
EDIT:
Some general suggestions:
don't load the question every time when the button is clicked.
make some Question class containing all needed information for a question
when onCreate is invoked, open the database and load all needed questions in some Collection, like ArrayList<Question>
Every time when the user clicks a button, get the next Question element from the Collection and show it

Categories

Resources