Calling a Method from button click - java

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

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.

Custom Autocomplete Adapter Android

I wish to have a auto-complete text-box which comes up with the users contact names. My code is as follows.
private void getContactNames()
{
Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null,null,null,null);
_contactAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line);
while (cursor.moveToNext())
{
int nameIdx = cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME);
String tmp = cursor.getString(nameIdx);
_contactAdapter.add(tmp);
}
}
Setting the adapter:
AutoCompleteTextView contactName = (AutoCompleteTextView) findViewById(R.id.contactName);
contactName.setAdapter(_contactAdapter);
When I do this, the adapter has all the contact names in there (238 contacts). However, when I start typing into the text box, the auto complete does not show.
Funny, as when I test it out doing this:
String[] ab = new String[] {"aaaaa", "bbbbb"};
_contactAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line,ab);
it will show "aaaaa" and "bbbbb" when typing in to the text box.
Any ideas?
Thanks,
Tom
*EDIT
Just thought I would follow up. It does seem to be the sheer amount of contacts that is preventing it from appearing. Any way to get around this?
while (cursor.moveToNext())
{
int nameIdx = cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME);
String tmp = cursor.getString(nameIdx);
//_contactAdapter.add(tmp);
// get all names in a new arraylist and then assign it to
arrayList.add(tmp);
}
and then assign it to your adapter as
_contactAdapter = new ArrayAdapter<String> this,android.R.layout.simple_dropdown_item_1line, arrayList);

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

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.

Android: IndexOutOfBounds Error when deleting row in ListView

I have an application that has 2 screens. The first screen has a ListView of movies with a row consisting of 3 Elements: Title, Date and Gross declared in strings.xml. The user has the option of adding a movie by clicking the menu button, which sends him to another screen. The second screen has 3 Edit texts that correspond to Title Date and Gross, which is alphabetically sorted straight away when it returns to screen 1.
Similarly, the user can also Edit/Delete entries by long clicking a row thatbrings up a context menu. The Edit function works like this:
a.) User long clicks Titanic and chooses Edit
b.) Row gets deleted, and user is brought to screen 2
c.) Edit texts are populated with the initial data from the deleted Row
d.) When user edits data, new movie is added at the bottom of the ListView.
The problem arises when the user deletes this new movie at the bottom of the ListView. Logcat gives a
java.lang.IndexOutOfBoundsException: Invalid index 50, size is 50
Here is my code (Take note I am using Perst to persist data, but I don;t think that won't really matter with my problem):
case R.id.contextedit:
Lab9_082588FetchDetails row = (Lab9_082588FetchDetails) getListView()
.getItemAtPosition(info.position);
Intent editData = new Intent(MovieList.this, Lab9_082588Edit.class);
String startTitle = row.getTitle();
String startGross = row.getGross();
String startDate = row.getDate();
editData.putExtra(Lab9_082588Edit.TITLE_STRING, startTitle);
editData.putExtra(Lab9_082588Edit.GROSS_STRING, startGross);
editData.putExtra(Lab9_082588Edit.DATE_STRING, startDate);
startActivityForResult(editData, MovieList.EDIT_MOVIE);
int posEdit = info.position;
String editTitle = results.get(info.position).getTitle();
results.remove(posEdit);
adapter.notifyDataSetChanged();
//Perst
Index<Lab9_082588FetchDetails> rootEdit = (Index<Lab9_082588FetchDetails>) db
.getRoot();
rootEdit.remove(editTitle, results.get((int) info.id));
db.setRoot(rootEdit);
return true;
Edit Class:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection using item.getItemId()
switch (item.getItemId()) {
case R.id.edit:
next();
break;
}
return true;
}
private void next() {
// TODO Auto-generated method stub
EditText movieTitle = (EditText) findViewById(R.id.etTitle);
EditText movieGross = (EditText) findViewById(R.id.etGross);
EditText movieDate = (EditText) findViewById(R.id.etDate);
String title = movieTitle.getText().toString();
String gross = movieGross.getText().toString();
String date = movieDate.getText().toString();
if ((title.length() > 0) && (gross.length() > 0)
&& (date.length() == 4)) {
Intent hobby = getIntent();
hobby.putExtra(Lab9_082588Edit.TITLE_STRING, title);
hobby.putExtra(Lab9_082588Edit.GROSS_STRING, gross);
hobby.putExtra(Lab9_082588Edit.DATE_STRING, date);
setResult(RESULT_OK, hobby);
finish();
}
}
Delete function:
int posDelete = info.position;
String deleteTitle = results.get(
info.position).getTitle();
results.remove(posDelete);
adapter.notifyDataSetChanged();
Index<Lab9_082588FetchDetails> rootDelete = (Index<Lab9_082588FetchDetails>) db
.getRoot();
rootDelete.remove(deleteTitle,
results.get(info.position));
db.setRoot(rootDelete); //Perst
return;
OnActivityResult (Edit):
case EDIT_MOVIE:
Lab9_082588FetchDetails edittedMovie = new Lab9_082588FetchDetails();
NumberFormat formatterEdit = new DecimalFormat("###,###,###");
edittedMovie.setTitle(data
.getStringExtra(Lab9_082588Add.TITLE_STRING));
edittedMovie.setGross("$"
+ formatterEdit.format(Double.parseDouble(data
.getStringExtra(Lab9_082588Add.GROSS_STRING))));
edittedMovie.setDate(data
.getStringExtra(Lab9_082588Add.DATE_STRING));
results.add(edittedMovie);
adapter.notifyDataSetChanged();
Populating the Listview:
for (int i = 0; i < items.size(); i++) {
Lab9_082588FetchDetails sr = new Lab9_082588FetchDetails();
sr.setTitle(items.get(i).getTitle());
sr.setGross(items.get(i).getGross());
sr.setDate(items.get(i).getDate());
results.add(sr);
Collections.sort(results, ignoreCaseStart);
}
How do I remedy this?
This problem occurs because in your delete function, you first remove the element from the results collection("results.remove(posDelete);"), and then, a few lines later, you call "results.get(info.position)" to fetch a parameter for the rootDelete.remove call, but which is already removed.
If the element is the last element of your collection, let's say the 50th element, the value for "info.position" is 50. You remove one element, so the number of elements is now 49. In the rootDelete.remove line you call results.get(50), which produces the error.

Showing text in Android ListAdapter on this specific case

I am trying to build a simple TV-Guide for Android. To make that, I am using RSS from one site. I parse XML and now, I'd like to show it. The idea is a List that displays Station, Whats currently on the show and date, and when I press on some item in the list it will give me a new Activity that shows the full schedule of some TV station. I've managed to separate parts of Whole TV program in just separate Strings (like: 06:00 News; 07:15 Movie). And I even managed to separate it so it fills String[][] (like: |06|,|00|,|News| ; |07|,|15|,|Movie|). And my Idea was to check which one is closest to real time and to display it.
Well, currently my App displays TV Station, WHOLE program (schedule), and Date. And I want it to display just whats currently showed. Can anybody help me with this one?
Here is class in which I want to do it: (I also have Parser class and Main class with just buttons and URLs of XMLs)
public class Pomocna extends ListActivity {
// All static variables
static String url =null;
// XML node keys
static final String KEY_ITEM = "item"; // parent node
static final String KEY_TITLE = "title";
static final String KEY_DATE = "pubDate";
static final String KEY_DESC = "encoded";
Calendar calendar = new GregorianCalendar();
int DSQ=calendar.get(Calendar.HOUR_OF_DAY);
int DMQ=calendar.get(Calendar.MINUTE);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent in = getIntent();
// Get XML values from previous intent
url = in.getStringExtra("A");
ArrayList<HashMap<String,String>> menuItems = new ArrayList<HashMap<String,String>>();
//ArrayList<String[][]> nekaj = new ArrayList<String[][]>();
XMLParser parser = new XMLParser();
String xml = parser.getXmlFromUrl(url); //get XML
Document doc = parser.getDomElement(xml); // get DOM elem.
//doc.getDocumentElement().normalize();
NodeList nl = doc.getElementsByTagName(KEY_ITEM);
//loop
for (int i=0; i< nl.getLength(); i++){
HashMap<String, String> map = new HashMap<String, String>();
Element e = (Element) nl.item(i);
//add to map
map.put(KEY_TITLE, Vracanje1(parser.getValue(e, KEY_TITLE)));
map.put(KEY_DATE, Vracanje2(parser.getValue(e, KEY_DATE)));
map.put(KEY_DESC, parser.getValue3(e,KEY_DESC));
//NEKO FRCERANJE -=- POCETAK
String pvp = parser.getValue3(e,KEY_DESC);
char[] rast = pvp.toCharArray();
int brojac = 0;
for(int q=0; q<pvp.length(); q++){
if(rast[q]=='*') brojac++;
}
String[][] niz= new String[brojac][3];
int bf1=0;
int bf3=0;
int oo=0;
for(int q=0; q<pvp.length(); q++){
if(rast[q]=='*'){
bf3=bf1;
bf1=q;
String lol = pvp.substring(bf3, bf1);
// SEPARATE STRING: LOL
// IT MUST HAVE 3 PARTS: HOUR, MINUTE, AND DESCRIPTION
if(oo==0){
String ps=lol.substring(0,2);
String ds=lol.substring(3,5);
String ts=lol.substring(6,lol.length());
niz[oo][0]=ps;
niz[oo][1]=ds;
niz[oo][2]=ts;
}
if(oo>0){
String ps=lol.substring(1,3);
String ds=lol.substring(4,6);
String ts=lol.substring(7,lol.length());
niz[oo][0]=ps;
niz[oo][1]=ds;
niz[oo][2]=ts;
}
oo++;
}
}
//NEKO FRCERANJE -=- KRAJ
menuItems.add(map);
//nekaj.add(niz);
}
//DISPLAY WHAT's CURRENTLY ON PROGRAM
//USE TIME TO COMPARE AND SHOW
ListAdapter adapter = new SimpleAdapter(this, menuItems, R.layout.list_item,
new String[]{KEY_TITLE, KEY_DESC, KEY_DATE}, new int[]{
R.id.title, R.id.description, R.id.date
});
setListAdapter(adapter);
//singleView
ListView lv = getListView();
}
public String Vracanje1(String bq){
int p1=bq.length();
int p2=p1-10;
StringBuilder sbp= new StringBuilder(bq);
sbp.delete(p2, p1);
String ses=sbp.toString();
return ses;
}
public String Vracanje2(String bq){
int p1=bq.length();
int p2=p1-14;
StringBuilder sbp=new StringBuilder(bq);
sbp.delete(p2, p1);
String ses = sbp.toString();
return ses;
}
}
Per my understanding the way You're doing won't be acceptable to any TV-program application, because usually program has many channels with many programs and parsing and analysing the data in UI thread will (sooner or later) cause ANRs. I would suggest You to consider use of separate service and content provider to let UI thread do only UI-related stuff and leave data processing to corresponding components. It would be faster and easier to find/process data via sqlite in provider.
Regarding a question about finding closest data - You could sort arrays and when find closest one (e.g. just iterating from the beginning).
I agree with #sandstar, but I'm not sure if what you're asking is how to filter the data or how to show the data? In this case it is the matter of updating the ListAdapter. This means, when you want to show only the most recent program, you need to provide the adapter a list of maps containing only the map with the data for the latest show. This means you should filter the data before applying the adapter to the ListView. If you want to have more control over the adapter you might consider extending the ArrayAdapter which also allows you to add/clear the data of the existing adapter.

Categories

Resources