Have at least one item selected in Android MultiSelectListPreference - java

I have search now for hours through the internet and have found nothing substantial so far. The thing that I want to do is a multi choice preference view, that disables the last item and reenables it, if it is not alone anymore.
I through so far about taking the super class force read the private variables in there to write my own onPrepareDialogBuilder(AlertDialog.Builder builder). Which is configuring its own OnMultiChoiceClickListener that jumps in, in the moment where has only one item left. The problem here is, that I use a bad practice force read of a private variable and that I have so far no idea how to get the checkbox item and how to disable it. But I think looking even deeper into the Android SDK will solve this problem.
At the end, if nothing works, solving the problem with doing an overwrite the OnPreferenceChangeListener to display a toast if the user has less than one item selected. But user friendliness is a high value, that needs to be earned and that often isn't easy.
Thx.
import android.content.Context;
import android.preference.MultiSelectListPreference;
import android.util.AttributeSet;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import georg.com.flironetest_01.Variables.Units;
/**
* Created by Georg on 16/03/16.
*/
public class UnitMultipleSelectorPreference extends MultiSelectListPreference {
public UnitMultipleSelectorPreference(Context context, AttributeSet attrs) {
super(context, attrs);
List<CharSequence> humanU = new ArrayList<>();
List<CharSequence> machineU = new ArrayList<>();
Units[] all = Units.values(); // Units is a enum with a rewriten to string statement.
for (Units elem : all) {
humanU.add(elem.toString());
machineU.add(elem.name());
}
setEntries(humanU.toArray(new CharSequence[humanU.size()]));
setEntryValues(machineU.toArray(new CharSequence[machineU.size()]));
Set<String> mU = new HashSet<>();
mU.add(Units.C.name());
mU.add(Units.K.name());
setDefaultValue(mU);
}
}

Okay. To answer my own question here after the motto "self is the man": I ended up with programming my own preference panel. Below is the code. If somebody likes to look over it and give some times how to make it even more stable: feel free.
But to sum up what I did: I created my own ArrayAdapter. But DialogPreference didn't allowed me to create my own multi selector. You need to change the final dialog fragment to create a working multi selector list (see here: https://stackoverflow.com/a/17907379/5759814). That is not an easy task if you work with the DialogPreferences. The reason is these few amounts of code:
/**
* Shows the dialog associated with this Preference. This is normally initiated
* automatically on clicking on the preference. Call this method if you need to
* show the dialog on some other event.
*
* #param state Optional instance state to restore on the dialog
*/
protected void showDialog(Bundle state) {
Context context = getContext();
mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
mBuilder = new AlertDialog.Builder(context)
.setTitle(mDialogTitle)
.setIcon(mDialogIcon)
.setPositiveButton(mPositiveButtonText, this)
.setNegativeButton(mNegativeButtonText, this);
View contentView = onCreateDialogView();
if (contentView != null) {
onBindDialogView(contentView);
mBuilder.setView(contentView);
} else {
mBuilder.setMessage(mDialogMessage);
}
onPrepareDialogBuilder(mBuilder);
getPreferenceManager().registerOnActivityDestroyListener(this);
// Create the dialog
final Dialog dialog = mDialog = mBuilder.create();
if (state != null) {
dialog.onRestoreInstanceState(state);
}
if (needInputMethod()) {
requestInputMethod(dialog);
}
dialog.setOnDismissListener(this);
dialog.show();
}
As you can see here is a method triggered to change my dialog builder with onPrepareDialogBuilder, but it doesn't seem like that there is any other function triggered afterwards, that would allow me to change the dialog directly after its creation. And the second idea of changing the onPrepareDialogBuilder so that I can init everything there, doesn't really help, because I end up with displayed dialog windows. That lead me to my decision of creating my completely own Preference class. With that decision I loose all those nice prepared functions like onRestoreInstanceState and Co, but I now have an application with a much more persistent flow, that doesn't do any stupid things when I select zero units for my thermal view.
Below the non commented code. I'm sorry, but I think its simple enough for everybody who landing here.
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.preference.Preference;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import georg.com.flironetest_01.Variables.Units;
/**
* Created by Georg on 16/03/16.
*/
public class UnitMultipleSelectorPreference extends Preference implements DialogInterface.OnClickListener, Preference.OnPreferenceClickListener {
String[] human_entries = null;
String[] machine_entries = null;
public SharedPreferences prev;
public UnitMultipleSelectorPreference(Context context, AttributeSet attrs) {
super(context, attrs);
prev = getSharedPreferences();
List<String> humanU = new ArrayList<>();
List<String> machineU = new ArrayList<>();
Units[] all = Units.values();
for (Units elem : all) {
humanU.add(elem.toString());
machineU.add(elem.name());
}
human_entries = humanU.toArray(new String[humanU.size()]);
machine_entries = machineU.toArray(new String[machineU.size()]);
Set<String> mU = new HashSet<>();
mU.add(Units.C.name());
mU.add(Units.K.name());
setDefaultValue(mU);
setOnPreferenceClickListener(this);
}
boolean[] selected = new boolean[0];
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
if (prev == null)
return;
if (human_entries == null || machine_entries == null || human_entries.length != machine_entries.length ) {
throw new IllegalStateException(
"ListPreference requires an entries array and an entryValues array which are both the same length");
}
selected = new boolean[human_entries.length];
for (int i = 0; i < human_entries.length; i++)
selected[i] = prefSet.contains(machine_entries[i]);
String[] stringObj = new String[human_entries.length];
int i = 0;
for(CharSequence ch : human_entries)
stringObj[i++] = ch.toString();
builder.setAdapter(new MyAdapter(getContext(), android.R.layout.simple_list_item_multiple_choice, stringObj), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
AlertDialog mDialog = builder.create();
mDialog.getListView().setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
mDialog.getListView().setItemsCanFocus(false);
mDialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// Manage selected items here
ListView mParent = (ListView)parent;
if (mParent.getCheckedItemCount() >= 1)
selected[position] = mParent.isItemChecked(position);
if (mParent.getCheckedItemCount() == 0)
mParent.setItemChecked(position, true);
}
});
mDialog.show();
i = 0;
for (boolean select : selected)
mDialog.getListView().setItemChecked(i++, select);
}
#Override
public boolean onPreferenceClick(Preference preference) {
AlertDialog.Builder mBuilder = new AlertDialog.Builder(getContext());
mBuilder.setTitle(getTitle())
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, this);
onPrepareDialogBuilder(mBuilder);
return true;
}
#Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getContext(), "W:"+which + " | " + Arrays.toString(selected), Toast.LENGTH_SHORT).show();
switch (which) {
case -1:
if (isPersistent()) {
prefSet = new HashSet<>();
for (int i = 0; i < selected.length; i++) {
if (selected[i])
prefSet.add(machine_entries[i]);
}
getEditor().putStringSet(getKey(), prefSet).apply();
Toast.makeText(getContext(), "W:"+which + " | " + getSharedPreferences().getStringSet(getKey(),null).toString(), Toast.LENGTH_SHORT).show();
}
return;
}
}
public class MyAdapter extends ArrayAdapter<String> {
public MyAdapter(Context context, int textViewResourceId, String[] objects) {
super(context, textViewResourceId, objects);
}
#Override
public boolean isEnabled(int n) {
return true;
}
}
Set<String> prefSet;
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
super.onSetInitialValue(restorePersistedValue, defaultValue);
prev = getSharedPreferences();
if(restorePersistedValue) {
prefSet = prev.getStringSet(getKey(), new HashSet<String>());
} else {
try {
prefSet = (Set<String>)defaultValue;
if(isPersistent())
getEditor().putStringSet(getKey(), prefSet);
} catch (ClassCastException e) {
Log.e("ERROR_CAST", "Error casting the default value to Set<String>.");
}
}
}
}

A really simple solution is to set a setOnPreferenceChangeListener and just return false if the new value would be empty.
All of the code is put into onCreatePreferences.
MultiSelectListPreference infoPreference = findPreference("information");
infoPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (size(newValue) == 0){
return false;
}
return true;
}
});

Related

getResources on a null object in an android project

I have a method in a library I am creating for a series of apps I am doing. This method will display an input dialog for the user to input a string entry. I am writing this project for android using android studio, and I am writing it in Java.
Here is the Logcat:
2020-11-17 00:44:23.819 6758-6758/com.phoenixhosman.launcher E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.phoenixhosman.launcher, PID: 6758
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
at android.content.ContextWrapper.getResources(ContextWrapper.java:91)
at android.view.ContextThemeWrapper.getResourcesInternal(ContextThemeWrapper.java:127)
at android.view.ContextThemeWrapper.getResources(ContextThemeWrapper.java:121)
at android.content.Context.getString(Context.java:594)
at com.phoenixhosman.phoenixlib.ActivityPhoenixLib.InputDialog(ActivityPhoenixLib.java:97)
at com.phoenixhosman.launcher.ActivityHome.onClick(ActivityHome.java:151)
at android.view.View.performClick(View.java:6603)
at android.view.View.performClickInternal(View.java:6576)
at android.view.View.access$3100(View.java:780)
at android.view.View$PerformClick.run(View.java:26090)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6714)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:503)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:911)
Here in my ActivityHome.java file:
/*
The Phoenix Hospitality Management System
Launcher App Source Code
Main Activity Code File
Copyright (c) 2020 By Troy Marker Enterprises
All Rights Under Copyright Reserved
The code in this file was created for use with the Phoenix Hospitality Management System (PHMS).
Use of this code outside the PHMS is strictly prohibited.
*/
package com.phoenixhosman.launcher;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.phoenixhosman.phoenixapi.*;
import com.phoenixhosman.phoenixlib.ProviderUser;
import com.phoenixhosman.phoenixlib.ActivityPhoenixLib;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.concurrent.atomic.AtomicInteger;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import static com.phoenixhosman.phoenixapi.ManagerSecurityApi.*;
/**
* This activity display links to the other apps in the Phoenix Hospitality
* system, and use authentication to only show those that the user has
* permission to use.
* #author Troy L. Marker
* #version 1.0.0
* #since 0.5.0
*/
public class ActivityHome extends Activity implements View.OnClickListener
{
private String strCoName;
private String strApiUrl;
private String strLockPass;
private EditText etUsername;
private EditText etPassword;
final private static int REQUEST_CODE_1 = 1;
final ActivityPhoenixLib Phoenix = new ActivityPhoenixLib();
/**
* This method will create the activity, read content to display, and show the main activity screen
* Used when the activity is (re)created.
* #param savedInstanceState current saved instance state
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
setContentView(R.layout.activity_home);
etUsername = findViewById(R.id.etUsername);
etPassword = findViewById(R.id.etPassword);
TextView tvWarning = findViewById(R.id.tvWarning);
Button btnLogon = findViewById(R.id.btnLogon);
Button btnLockPass = findViewById(R.id.btnLockPass);
btnLogon.setOnClickListener(this);
btnLockPass.setOnClickListener(this);
#SuppressLint("Recycle") Cursor cursor = getContentResolver().query(Uri.parse("content://com.phoenixhosman.installer.ProviderSettings/settings"), null, null, null, null, null);
assert cursor != null;
if(cursor.moveToFirst()) {
while(!cursor.isAfterLast()) {
strCoName = cursor.getString(cursor.getColumnIndex("coname"));
strApiUrl = cursor.getString(cursor.getColumnIndex("apiurl"));
strLockPass = cursor.getString(cursor.getColumnIndex("lockpass"));
cursor.moveToNext();
}
new ManagerSecurityApi(strApiUrl);
tvWarning.setText(getString(R.string.warning, rtrim(strCoName)));
} else {
Phoenix.Error(getApplicationContext(),getString(R.string.required), false);
}
}
/**
* Override of the onBackPress method from the parent class
* This disables the back button on the device.
*/
#Override
public void onBackPressed() {
Phoenix.Error(ActivityHome.this, getString(R.string.disabled, getString(R.string.back)),false);
}
/**
* This method overrides the parents click listner.
* #param v the view clicked.
*/
#Override
public void onClick(View v) {
Button button = (Button)v;
String buttonText = button.getText().toString();
if (buttonText.equals(getString(R.string.login))) {
if (etUsername.getText().toString().isEmpty() || etPassword.getText().toString().isEmpty()) {
Phoenix.Error(ActivityHome.this, getString(R.string.both_required,getString(R.string.username_password)), false);
} else {
Call<String> call = getInstance().getApi().login(etUsername.getText().toString(), etPassword.getText().toString());
call.enqueue(new Callback<String>() {
#Override
public void onResponse(#NonNull Call<String> call, #NonNull Response<String> response) {
String body = response.body();
try {
assert body != null;
JSONObject obj = new JSONObject(body);
if (obj.optBoolean("success")) {
etUsername.setText("");
etPassword.setText("");
etUsername.requestFocus();
getContentResolver().delete(ProviderUser.CONTENT_URI, null, null);
ContentValues values = new ContentValues();
values.put(ProviderUser.name, obj.optString("username"));
values.put(ProviderUser.grade, obj.optInt("grade"));
values.put(ProviderUser.gradename, obj.optString("gradename"));
values.put(ProviderUser.department, obj.optInt("department"));
values.put(ProviderUser.departmentname, obj.optString("departmentname"));
getContentResolver().insert(ProviderUser.CONTENT_URI, values);
Phoenix.Success(ActivityHome.this, obj.optString("message"), 5);
showApps(obj.optString("username"), obj.optInt("grade"), obj.optString("gradename"), obj.optInt("department"), obj.optString("departmentname"));
} else {
Phoenix.Error(getApplicationContext(), getString(R.string.user_not), false);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(#NonNull Call<String> call, #NonNull Throwable t) {
}
});
}
} else if (buttonText.equals(getString(R.string.admin_access))) {
String LockPass = Phoenix.InputDialog(ActivityHome.this.getApplicationContext(), getString(R.string.input_prompt, getString(R.string.enter_lock_pass)));
if (LockPass.equals(strLockPass)) {
this.getPackageManager().clearPackagePreferredActivities(this.getPackageName());
finish();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
} else {
throw new IllegalStateException(getString(R.string.unexpected) + v.getId());
}
}
/**
* This method will call the App Display Activity
*/
private void showApps(String username, Integer grade, String gradename, Integer department, String departmentname){
Intent intent = new Intent(ActivityHome.this.getApplicationContext(), ActivityApp.class);
intent.putExtra("username", username);
intent.putExtra("grade", String.valueOf(grade));
intent.putExtra("gradename", gradename);
intent.putExtra("department", String.valueOf(department));
intent.putExtra("departmentname", departmentname);
startActivityForResult(intent, REQUEST_CODE_1);
}
/**
* Function to trim whitespace from the end of a string.
* #param s the string to trim
* #return the trimmed string
*/
#NonNull
public static String rtrim(String s) {
AtomicInteger i;
i = new AtomicInteger(s.length() - 1);
while (i.get() >= 0 && Character.isWhitespace(s.charAt(i.get()))) {
i.getAndDecrement();
}
return s.substring(0, i.get() +1);
}
}
And finially, here is my ActivityPhoenixLib.java file:
package com.phoenixhosman.phoenixlib;
import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import static android.view.View.inflate;
public class ActivityPhoenixLib extends Activity {
/**
* While not needed, this constructor makes calls to the member method of the class
* to clear errors in the editor.
*/
public ActivityPhoenixLib() {
Error(this, "", false);
Success(this,"",0);
String dummy = InputDialog(this, "");
if (dummy.equals("test")) {
}
}
/**
* This method displays a dialog box with an error message and a close button.
* #param strError the error message to display
*/
public void Error(Context context, #NonNull String strError, Boolean exit) {
if (strError.equals("")) {
return;
}
AlertDialog.Builder mBuilder = new AlertDialog.Builder(context);
View view = inflate(context, R.layout.dialog_error, null);
Button btnExit = view.findViewById(R.id.btnExitButton);
Button btnError = view.findViewById(R.id.btnErrorMessage);
btnError.setText(strError);
mBuilder.setView(view);
AlertDialog dialog = mBuilder.create();
dialog.setCanceledOnTouchOutside(false);
dialog.setCancelable(false);
dialog.show();
btnExit.setOnClickListener(v -> {
dialog.dismiss();
if (exit) System.exit(0);
});
}
/**
* This method displays a dialog box with a message and a close button.
* It also has an auto close function to auto close after a specified number of seconds.
* #param strMessage the message to display
*/
public void Success(Context context, #NonNull String strMessage, Integer autoclose) {
if (strMessage.equals("")) {
return;
}
AlertDialog.Builder mBuilder = new AlertDialog.Builder(context);
View view = inflate(context, R.layout.dialog_success, null);
Button btnExit = view.findViewById(R.id.btnButton);
Button btnError = view.findViewById(R.id.btnMessage);
btnError.setText(strMessage);
mBuilder.setView(view);
AlertDialog dialog = mBuilder.create();
dialog.setCanceledOnTouchOutside(false);
dialog.setCancelable(false);
dialog.show();
btnExit.setOnClickListener(v -> dialog.dismiss());
new Handler().postDelayed(dialog::dismiss, autoclose * 1000);
}
/**
* This method show an input box to get string input from the user.
* #param strPrompt - the dialog prompt
*/
public String InputDialog(Context context, String strPrompt) {
if (strPrompt.equals("")) {
return "";
}
AlertDialog.Builder mBuilder = new AlertDialog.Builder(context);
final String[] retval = new String[1];
View view = inflate(context, R.layout.dialog_input, null);
TextView txtPrompt = view.findViewById(R.id.txtPrompt);
EditText edtLockPass = view.findViewById(R.id.edtLockPass);
Button btnEnter = view.findViewById(R.id.btnEnter);
--> Line 97 <-- txtPrompt.setText(getString(R.string.input_prompt, strPrompt));
mBuilder.setView(view);
AlertDialog dialog = mBuilder.create();
dialog.setCanceledOnTouchOutside(false);
dialog.setCancelable(false);
dialog.show();
btnEnter.setOnClickListener(v -> {
if (edtLockPass.getText().toString().equals("")) {
retval[0] = "";
} else {
retval[0] = edtLockPass.getText().toString();
}
});
dialog.dismiss();
return retval[0];
}
}
I have tried several different things. I am going on the assumption that the context is not being passed to the InputDialog function, and I have tried different way of pass the context to the function: this. ActivityHome.this, getApplicationContext, and none of them seemed to work. Is there something I have nbot considered? Any help would be appreciated. Thanks.
Edit-In responce to one of the comments about my getString usage, on the page linked here my usage is documented, the second string is is the argument object for the formatting string.
Edit 2: I also tried to use a single parameter getSting on the line in question, and got the smae error.

Arraylist with multi answers android studio quiz app

I am making a quiz application and using the ArrayList with. I have a problem with the Answers: it is working when I have just one answer, but what can I do if the question has two answers?
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import cn.pedant.SweetAlert.SweetAlertDialog;
public class MainActivity extends AppCompatActivity {
TextView questionLabel, questionCountLabel, scoreLabel;
EditText answerEdt;
Button submitButton;
ProgressBar progressBar;
ArrayList<QuestionModel> questionModelArraylist;
int currentPosition = 0;
int numberOfCorrectAnswer = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
questionCountLabel = findViewById(R.id.noQuestion);
questionLabel = findViewById(R.id.question);
scoreLabel = findViewById(R.id.score);
answerEdt = findViewById(R.id.answer);
submitButton = findViewById(R.id.submit);
progressBar = findViewById(R.id.progress);
questionModelArraylist = new ArrayList<>();
setUpQuestion();
setData();
submitButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
checkAnswer();
}
});
answerEdt.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the event is a key-down event on the "enter" button
Log.e("event.getAction()",event.getAction()+"");
Log.e("event.keyCode()",keyCode+"");
if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
(keyCode == KeyEvent.KEYCODE_ENTER)) {
checkAnswer();
return true;
}
return false;
}
});
}
public void checkAnswer(){
String answerString = answerEdt.getText().toString().trim();
if(answerString.equalsIgnoreCase(questionModelArraylist.get(currentPosition).getAnswer())){
numberOfCorrectAnswer ++;
new SweetAlertDialog(MainActivity.this, SweetAlertDialog.SUCCESS_TYPE)
.setTitleText("Sehr gut!")
.setContentText("Richtig!")
.setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() {
#Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
currentPosition ++;
setData();
answerEdt.setText("");
sweetAlertDialog.dismiss();
}
})
.show();
}else {
new SweetAlertDialog(MainActivity.this, SweetAlertDialog.ERROR_TYPE)
.setTitleText("Falsch :(")
.setContentText("Die Richtige Antwort ist : "+questionModelArraylist.get(currentPosition).getAnswer())
.setConfirmText("OK")
.setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() {
#Override
public void onClick(SweetAlertDialog sDialog) {
sDialog.dismiss();
currentPosition ++;
setData();
answerEdt.setText("");
}
})
.show();
}
int x = ((currentPosition+1) * 100) / questionModelArraylist.size();
progressBar.setProgress(x);
}
public void setUpQuestion(){
questionModelArraylist.add(new QuestionModel("Write one planet located between Earth and the sun","Venus or Mercury"));
questionModelArraylist.add(new QuestionModel("the 5th planet from the sun","Jupiter"));
questionModelArraylist.add(new QuestionModel("write names of any two oceans","Atlantic Ocean or Arctic Ocean or Indian Ocean or Pacific Ocean orSouthern Ocean"));
}
public void setData(){
if(questionModelArraylist.size()>currentPosition) {
questionLabel.setText(questionModelArraylist.get(currentPosition).getQuestionString());
scoreLabel.setText("Ergebnis :" + numberOfCorrectAnswer + "/" + questionModelArraylist.size());
questionCountLabel.setText("Frage Nummer : " + (currentPosition + 1));
}else{
new SweetAlertDialog(MainActivity.this, SweetAlertDialog.SUCCESS_TYPE)
.setTitleText("Du bist Fertig :)")
.setContentText("dein Ergebnis ist : "+ numberOfCorrectAnswer + "/" + questionModelArraylist.size())
.setConfirmText("Wiederholen")
.setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() {
#Override
public void onClick(SweetAlertDialog sDialog) {
sDialog.dismissWithAnimation();
currentPosition = 0;
numberOfCorrectAnswer = 0;
progressBar.setProgress(0);
setData();
}
})
.setCancelText("schließen")
.setCancelClickListener(new SweetAlertDialog.OnSweetClickListener() {
#Override
public void onClick(SweetAlertDialog sDialog) {
sDialog.dismissWithAnimation();
finish();
}
})
.show();
}
}
}
Question Class
public class QuestionModel {
public QuestionModel(String questionString, String answer) {
QuestionString = questionString;
Answer = answer;
}
public String getQuestionString() {
return QuestionString;
}
public void setQuestionString(String questionString) {
QuestionString = questionString;
}
public String getAnswer() {
return Answer;
}
public void setAnswer(String answer) {
Answer = answer;
}
private String QuestionString;
private String Answer;
}
As you can see, I want to make the answer in the first Question to be Mercury or Venus,the 2nd Question is Jupiter and the 3d Question has 5 Answers "Atlantic Ocean or Arctic Ocean or Indian Ocean or Pacific Ocean or Southern Ocean"
how can i make it to work ? Thank you.
Basically: by changing your data model.
You see, you create your classes to "model" reality. If you want to allow for multiple answers, then a simple one to one mapping (as QuestionModel implies) simply doesn't work. Then your model should be 1:n, like 1 question string, but the answers could be a List<String> for example. For questions with only one answer, that list simply contains only a single entry.
That is the way to go there: first you think up how your data needs to be organized, then you build everything, including your UI structures around that.
#GhostCat is correct, you must analize before implementing. For your specific situation i'm thinking that Enumerations might be helpful. I am not entirely clear about your goal but that is how i would initially approach it.
And to generalize it in your QuestionModel class I would first create an Interface to describe any possible answer.
interface AnswerInterface {
val value: String
}
I would use generics that extend this interface and my QuestionModel then would become as this.
class QuestionModel<QuestionAnswer : AnswerInterface>(
var question: String,
vararg answers: QuestionAnswer
)
The QuestionAnswer can be any class that implements the AnswerInterface and the varard means i can have any number of intances as answers.
With this in mind, my answers enum would be:
enum class PlanetAnswers(override val value: String) : AnswerInterface {
VENUS("Venus"),
MERCURY("Mercury"),
JUPITER("Jupiter")
}
Finally, everything ties together like this:
val question1 = QuestionModel("One planet between Earth and Sun?", PlanetAnswers.VENUS, PlanetAnswers.MERCURY)
val question2 = QuestionModel("The 5th planet from the Sun?", PlanetAnswers.JUPITER)
I have a question and any number of answers, but they all must come from the same enum. I can create more enums and then create more questions with different set of answers.

Accessing data from barcode scanner app to use in SQLite query

This is my first question here on stack overflow, so please forgive me for any oversight or formatting errors. This issue seems simple enough, but I am not able to "put the pieces together" for some reason. I am also learning java and android studio as I go, so please forgive and educate on any bad code.
I need to gather data from my barcode scanning app, submit it to a variable, and then pass that variable through my database to fetch information based on the UPC code. I am using the ZXing library for the barcode scanner, with the handleResult method to capture the initial data.
I have the data collected within the SimpleScanner activity, but I can't figure out how to use that variable in a SQlite query. Below are the main classes I am using.
Any help would be appreciated. I can query the entire database just fine, but I need to look up the rows that match the actual item I am scanning. Thanks again!
SimpleScannerActivity.java
package com.example.android.dropr;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
import com.google.zxing.Result;
import java.util.ArrayList;
import java.util.List;
import me.dm7.barcodescanner.zxing.ZXingScannerView;
public class SimpleScannerActivity extends MainActivity implements ZXingScannerView.ResultHandler {
private ZXingScannerView mScannerView;
#Override
public void onCreate(Bundle state) {
super.onCreate(state);
mScannerView = new ZXingScannerView(this); // Programmatically initialize the scanner view
setContentView(mScannerView); // Set the scanner view as the content view
}
#Override
public void onResume() {
super.onResume();
mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
mScannerView.startCamera(); // Start camera on resume
}
#Override
public void onPause () {
super.onPause();
mScannerView.stopCamera(); // Stop the camera on pause
}
#Override
public void handleResult(Result rawResult) {
String TAG = "Dropr";
/**
* Create Alert Dialog, so that user has time to read the information within.
*/
AlertDialog.Builder scanInfo = new AlertDialog.Builder(this);
String messageContent = "Content - " + rawResult.getText();
String messageFormat = "Format - " + rawResult.getBarcodeFormat().toString() + ".";
scanInfo.setTitle("Scan Information:");
scanInfo.setMessage(messageContent + "\n" + messageFormat);
scanInfo.setCancelable(true);
scanInfo.setPositiveButton(
"OK",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
// IF you would like to resume scanning, call this method below:
// Handle the data
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
mScannerView.resumeCameraPreview(SimpleScannerActivity.this);
}
}, 1000);
}
});
AlertDialog showInfo = scanInfo.create();
showInfo.show();
// Do something with the result here
Log.v(TAG, rawResult.getText());
Log.v(TAG, rawResult.getBarcodeFormat().toString());
}
}
DatabaseAccess.java
package com.example.android.dropr;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.ArrayList;
import java.util.List;
public class DatabaseAccess {
private SQLiteOpenHelper openHelper;
private SQLiteDatabase database;
private static DatabaseAccess instance;
private SimpleScannerActivity scannerActivity = new SimpleScannerActivity();
/**
* Private constructor to avoid object creation from outside classes.
*
* #param context
*/
protected DatabaseAccess(Context context) {
this.openHelper = new DatabaseOpenHelper(context);
}
/**
* Return a singleton instance of DatabaseAccess.
*
* #param context
* #return the instance of DatabaseAccess
*/
public static DatabaseAccess getInstance(Context context) {
if (instance == null) {
instance = new DatabaseAccess(context);
}
return instance;
}
/**
* Open the database connection
*/
public void open() {
this.database = openHelper.getWritableDatabase();
}
/**
* Close the database connection
*/
public void close() {
if (database != null) {
this.database.close();
}
}
/**
* Read all quotes from the database.
*
* #return a list of quotes
*/
public List<String> getCodes() {
List<String> list = new ArrayList<>();
Cursor cursor = database.rawQuery("SELECT name, upc14 FROM Barcodes", null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
list.add(cursor.getString(0));
list.add(cursor.getString(1));
cursor.moveToNext();
}
cursor.close();
return list;
}
}
I finally came up with a solution, thanks to #muratgu! I created another method that creates and stores a variable for the scanned data, and passes the variable through a query.
/**
* read a single record from the database the matches the UPC-A code scanned.
* if there is no match, do nothing.
* #param rawContent
* #return a brand name based on the matching UPC-A code that was scanned.
*/
public String getInfo(String rawContent) {
String TAG = "Getinfo():";
String content = "00" + rawContent;
String brandName = "";
Cursor cursor = database.rawQuery("SELECT name, upc12 from Barcodes WHERE '" + content + "' = upc12", null);
if(cursor.getCount() > 0) {
cursor.moveToFirst();
brandName = cursor.getString(cursor.getColumnIndex("name"));
cursor.close();
} else {
Log.v(TAG, "uh oh, something went wrong in the if loop! ");
}
return brandName;
}
This method gets called in the SimpleScannerActivity.java file, where the scanned data can be passed through the variable. The method returns the name of the item, which is then placed in the dialog box. Exactly what I needed.
Thanks again, #muratgu! you gave me enough information that I could solve the problem myself. I just had to think on it for a bit!

Android - cannot scroll up

First off, I first laid eyes on Java three weeks ago so bear with me if this code is terrible. It's an assignment for school that I am to build on a prototyped app and give it a UI, so the Adapter is basically all I've done to this.
My problem being that as soon as I touch the scroll, I get thrown to the bottom of the list and can't scroll back up without getting pushed back down.
/**
* VaxjoWeather.java
* Created: May 9, 2010
* Jonas Lundberg, LnU
*/
package dv106.weather;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import android.app.ListActivity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
/**
* This is a first prototype for a weather app. It is currently
* only downloading weather data for Växjo.
*
* This activity downloads weather data and constructs a WeatherReport,
* a data structure containing weather data for a number of periods ahead.
*
* The WeatherHandler is a SAX parser for the weather reports
* (forecast.xml) produced by www.yr.no. The handler constructs
* a WeatherReport containing meta data for a given location
* (e.g. city, country, last updated, next update) and a sequence
* of WeatherForecasts.
* Each WeatherForecast represents a forecast (weather, rain, wind, etc)
* for a given time period.
*
* The next task is to construct a list based GUI where each row
* displays the weather data for a single period.
*
*
* #author jlnmsi
*
*/
public class VaxjoWeather extends ListActivity {
//private InputStream input;
private WeatherReport report = null;
//private ArrayList<WeatherForecast> forecastList = new ArrayList<WeatherForecast>();
private WeatherAdapter adapter;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
adapter = new WeatherAdapter(this);
setListAdapter(adapter);
//getListView().setTranscriptMode(ListView.TRANSCRIPT_MODE_DISABLED);
try {
URL url = new URL("http://www.yr.no/sted/Sverige/Kronoberg/V%E4xj%F6/forecast.xml");
AsyncTask task = new WeatherRetriever().execute(url);
} catch (IOException ioe ) {
ioe.printStackTrace();
}
//adapter.notifyDataSetChanged();
}
private void PrintReportToConsole() {
if (this.report != null) {
/* Print location meta data */
//System.out.println(report);
/* Print forecasts */
int count = 0;
for (WeatherForecast forecast : report) {
count++;
adapter.add(forecast);
}
}
else {
System.out.println("Weather report has not been loaded.");
}
//adapter.notifyDataSetChanged();
}
private class WeatherRetriever extends AsyncTask<URL, Void, WeatherReport> {
protected WeatherReport doInBackground(URL... urls) {
try {
return WeatherHandler.getWeatherReport(urls[0]);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void onProgressUpdate(Void... progress) {
}
protected void onPostExecute(WeatherReport result) {
report = result;
PrintReportToConsole();
}
}
// custom ArrayAdpater to show, weather icon, temperature, and precipation.
class WeatherAdapter extends ArrayAdapter<WeatherForecast> {
public WeatherAdapter(Context context) {
super(context,R.layout.forecast);
}
#Override // Called when updating the ListView
public View getView(int position, View convertView, ViewGroup parent) {
View row;
if (convertView == null) { // Create new row view object
LayoutInflater inflater = getLayoutInflater();
row = inflater.inflate(R.layout.forecast,parent,false);
}
else // reuse old row view to save time/battery
row = convertView;
// TextView for Temperature
TextView temperature = (TextView)row.findViewById(R.id.temperature);
temperature.setText(Integer.toString(this.getItem(position).getTemp())+" °C");
// TextView for out Precipation.
TextView precipation = (TextView)row.findViewById(R.id.rain);
precipation.setText(String.valueOf(this.getItem(position).getRain())+" mm");
// Image Icon for forecast.
ImageView icon = (ImageView)row.findViewById(R.id.icon);
String iconPath = "ic_";
if (this.getItem(position).getWeatherCode() <= 9){
iconPath = iconPath+"0"+(Integer.toString(this.getItem(position).getWeatherCode()));
}
else {
iconPath = iconPath+(Integer.toString(this.getItem(position).getWeatherCode()));
}
int resId = getResources().getIdentifier(iconPath, "drawable", getPackageName());
// If the resource ID is invalid, as in the image not existing, we'll add the postfix for periods.
if (resId == 0){
// Set the icon image source dependent on period code given.
if(this.getItem(position).getPeriodCode() == 3){
iconPath = iconPath +"n";
}
else if (this.getItem(position).getPeriodCode() == 2){
iconPath = iconPath +"d";
}
else {
iconPath = iconPath +"m";
}
resId = getResources().getIdentifier(iconPath, "drawable", getPackageName());
icon.setImageResource(resId);
}
// Or if everything checked out, we'll just run with the resource ID and find our Icon.
else {
icon.setImageResource(resId);
}
return row;
}
}
}
I tried applying another standard arrayadapter and actually got the same unwanted scrolling results, so I got no idea what part it is I got issues with.
Way to do it is:
1: Place ListView in main.xml
2: In your Activity Make object of ListView like so -> private ListView listView; in onCreate connect it to the main.xml like this, listView = (ListView) R.id.listView1; // whatever it is called
3: next create an ArrayList:-> private ArrayList arrayWeather = new ArrayList();
4: fill the arraylist with weather data then finally create object of class you created and make it use your arraylist to display data.
Example:
public class ListUser extends BaseAdapter{
#Override
public int getCount() {
// TODO Auto-generated method stub
return arraylistData.size(); // the arraylist u created
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return arraylistData.get(position);
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return arraylistData.size();
}
#Override
public View getView(final int position, View v, ViewGroup parent) {
if(v == null){
LayoutInflater lf = (LayoutInflater) BuyPets.this.getSystemService( Context.LAYOUT_INFLATER_SERVICE);
v = lf.inflate(R.layout.forecast, null);
}
// setup here, done
return v;
}
}
Solution was found and apparently it had nothing to do with my code. Class suggested we'd use Intel x86 instead of ARM for our emulators. Running it with ARM scrolling work just as expected.

Bug with ArrayAdapter and Search function?

When I search for a value in my list of names, it returns the corresponding value. But when I delete this value, my ListView does not return all the names on the list again, they disappear. How to solve this? Any idea?
Code of TabelaActivity.java:
package com.akzonobel.malote.tabela;
import com.akzonobel.malote.R;
import android.app.Activity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import android.widget.ListView;
/*
* Very basic Activity, the only things it does
* are get the ListView reference from our layout.
* Create an Adapter, set the Adapter to the ListView
* and handle the onItemClick events for when the user
* clicks on a row.
*/
public class TabelaActivity extends Activity {
EditText inputSearch;
CSVAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tabela);
//Lookup our ListView
ListView mList = (ListView)findViewById(R.id.mList);
inputSearch = (EditText) findViewById(R.id.inputSearch);
//Create Adapter. The second parameter is required by ArrayAdapter
//which our Adapter extends. In this example though it is unused,
//so we'll pass it a "dummy" value of -1.
mAdapter = new CSVAdapter(this, -1);
//attach our Adapter to the ListView. This will populate all of the rows.
mList.setAdapter(mAdapter);
/*
* This listener will get a callback whenever the user clicks on a row.
* The pos parameter will tell us which row got clicked.
*
* For now we'll just show a Toast with the state capital for the state that was clicked.
*/
inputSearch.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
// When user changed the Text
TabelaActivity.this.mAdapter.getFilter().filter(cs);
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable arg0) {
// TODO Auto-generated method stub
}
});
}
}
Code of CSVAdapter.java:
package com.akzonobel.malote.tabela;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.TextView;
/*
* Very basic Custom Adapter that takes state name,capital pairs out of a csv
* file from the assets and uses those values to build a List of State objects.
* Overrides the default getView() method to return a TextView with the state name.
*
* ArrayAdapter - a type of Adapter that works a lot like ArrayList.
*/
#SuppressLint("DefaultLocale") public class CSVAdapter extends ArrayAdapter<State>{
private List<State> filteredModelItemsArray;
private Filter filter;
Context ctx;
//We must accept the textViewResourceId parameter, but it will be unused
//for the purposes of this example.
public CSVAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
//Store a reference to the Context so we can use it to load a file from Assets.
this.ctx = context;
//Load the data.
loadArrayFromFile();
}
#Override
public Filter getFilter() {
if (filter == null){
filter = new ModelFilter();
}
return filter;
}
/*
* getView() is the method responsible for building a View out of a some data that represents
* one row within the ListView. For this example our row will be a single TextView that
* gets populated with the state name.
* (non-Javadoc)
* #see android.widget.ArrayAdapter#getView(int, android.view.View, android.view.ViewGroup)
*/
#Override
public View getView(final int pos, View convertView, final ViewGroup parent){
/*
* Using convertView is important. The system will pass back Views that have been
* created but scrolled off of the top (or bottom) of the screen, and thus are no
* longer being shown on the screen. Since they are unused, we can "recycle" them
* instead of creating a new View object for every row, which would be wasteful,
* and lead to poor performance. The diference may not be noticeable in this
* small example. But with larger more complex projects it will make a significant
* improvement by recycling Views rather than creating new ones for each row.
*/
TextView mView = (TextView)convertView;
//If convertView was null then we have to create a new TextView.
//If it was not null then we'll re-use it by setting the appropriate
//text String to it.
if(null == mView){
mView = new TextView(parent.getContext());
mView.setTextSize(19);
mView.setTextColor(Color.WHITE);
}
//Set the state name as the text.
mView.setText(getItem(pos).getName());
//We could handle the row clicks from here. But instead
//we'll use the ListView.OnItemClickListener from inside
//of MainActivity, which provides some benefits over doing it here.
return mView;
}
/*
* Helper method that loads the data from the states.csv and builds
* each csv row into a State object which then gets added to the Adapter.
*/
private void loadArrayFromFile(){
try {
// Get input stream and Buffered Reader for our data file.
InputStream is = ctx.getAssets().open("states.csv");
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
//Read each line
while ((line = reader.readLine()) != null) {
//Create a State object for this row's data.
State cur = new State();
cur.setName(line);
//Add the State object to the ArrayList (in this case we are the ArrayList).
this.add(cur);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private class ModelFilter extends Filter
{
#Override
protected FilterResults performFiltering(CharSequence constraint) {
constraint = constraint.toString().toLowerCase();
FilterResults result = new FilterResults();
if(constraint != null && constraint.toString().length() > 0)
{
ArrayList<State> filteredItems = new ArrayList<State>();
for(int i = 0, l = getCount(); i < l; i++)
{
State m = getItem(i);
if(m.getName().toLowerCase().contains(constraint))
filteredItems.add(m);
}
result.count = filteredItems.size();
result.values = filteredItems;
}
else
{
ArrayList<State> allItems = new ArrayList<State>();
for(int i = 0, l = getCount(); i < l; i++)
{
State m = getItem(i);
allItems.add(m);
}
synchronized(this)
{
result.values = allItems;
result.count = allItems.size();
}
}
return result;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
filteredModelItemsArray = (ArrayList<State>)results.values;
notifyDataSetChanged();
clear();
for(int i = 0, l = filteredModelItemsArray.size(); i < l; i++)
add(filteredModelItemsArray.get(i));
notifyDataSetInvalidated();
}
}
}
Code of State.java:
package com.akzonobel.malote.tabela;
/*
* Basic Data class to hold a state name and the state capital.
*/
public class State {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
This is a fairly old bug that's never been addressed by Android. It was first reported here, Issue 9666, then closed and reopened as Issue 69179. I wouldn't hold my breath on it being fixed anytime soon. If you're curious to read more on the details of why, I wrote a small blog post talking about it.
More or less, if you want to filter your data and also add, remove, update, etc portions of it...you'll need to extend BaseAdapter and write your own solution. Or save yourself some time and take advantage of my frustrations in doing that some many times over and over that I just threw it all into an opensource library. Solves all the filtering bugs and then some: Advanced-Adapters.

Categories

Resources