Bug with ArrayAdapter and Search function? - java

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.

Related

How to refresh content using Swipe Refresh Layout?

I am learning networking from UDACITY, there I came across an app named Quake Report, I wanted to add Swipe to refresh feature in the app but I am not able to get the results I want.
package com.example.android.quakereport;
import android.app.LoaderManager;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class EarthquakeActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<Earthquake>> {
/**
* Constant value for the earthquake loader ID. We can choose any integer.
* This really only comes into play if you're using multiple loaders.
*/
private static final int EARTHQUAKE_LOADER_ID = 1;
// TextView that is displayed when the list is empty
private TextView mEmptyStateTextView;
// progress bar
private ProgressBar progressBar;
public static final String LOG_TAG = EarthquakeActivity.class.getName();
// URL for earthquake data from the USGS dataset
private static final String USGS_REQUEST_URL =
"https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&orderby=time&minmag=5&limit=15";
/**
* Adapter for the list of earthquakes
*/
private EarthquakeAdapter mAdapter;
#Override
public Loader<List<Earthquake>> onCreateLoader(int id, Bundle args) {
Log.i(LOG_TAG, "Test: onCreateLoader() called...");
// Create a new loader for the given URL
return new EarthquakeLoader(this, USGS_REQUEST_URL);
}
#Override
public void onLoadFinished(Loader<List<Earthquake>> loader, List<Earthquake> data) {
Log.i(LOG_TAG, "Test: onLoadFinished() called...");
// Clear the adapter of previous earthquake data
mAdapter.clear();
// Hiding the progress bar as the results have been loaded
progressBar.setVisibility(View.GONE);
// Check if Internet Connection is present or not and show text accordingly
ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connManager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
mEmptyStateTextView.setText(R.string.no_earthquakes);
} else {
mEmptyStateTextView.setText(R.string.no_internet_connection);
}
// If there is a valid list of {#link Earthquake}s, then add them to the adapter's
// data set. This will trigger the ListView to update.
if (data != null && !data.isEmpty()) {
mAdapter.addAll(data);
}
}
#Override
public void onLoaderReset(Loader<List<Earthquake>> loader) {
Log.i(LOG_TAG, "Test: onLoaderReset() called...");
// Loader reset, so we can clear out our existing data.
mAdapter.clear();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.i(LOG_TAG, "Test: Earthquake Activity onCreate() called.");
super.onCreate(savedInstanceState);
setContentView(R.layout.earthquake_activity);
// Creating a swipe to refresh feature
final SwipeRefreshLayout pullToRefresh = findViewById(R.id.swipe_refresh);
pullToRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
pullToRefresh.setRefreshing(false);
}
});
// Find a reference to the {#link ListView} in the layout
ListView earthquakeListView = (ListView) findViewById(R.id.list);
// Get a reference to the ConnectivityManager to check state of network connectivity
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
// Get details on the currently active default data network
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
// If there is a network connection, fetch data
if (networkInfo != null && networkInfo.isConnected()) {
// Get a reference to the LoaderManager, in order to interact with loaders.
LoaderManager loaderManager = getLoaderManager();
// Initialize the loader. Pass in the int ID constant defined above and pass in null for
// the bundle. Pass in this activity for the LoaderCallbacks parameter (which is valid
// because this activity implements the LoaderCallbacks interface).
// Create a new adapter that takes an empty list of earthquakes as input
Log.i(LOG_TAG, "Test: calling initLoader()...");
loaderManager.initLoader(EARTHQUAKE_LOADER_ID, null, this);
} else {
// Otherwise, display error
// First, hide loading indicator so error message will be visible
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
progressBar.setVisibility(View.GONE);
// Update empty state with no connection error message
mEmptyStateTextView = (TextView) findViewById(R.id.empty_view);
mEmptyStateTextView.setText(R.string.no_internet_connection);
}
mEmptyStateTextView = (TextView) findViewById(R.id.empty_view);
earthquakeListView.setEmptyView(mEmptyStateTextView);
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
mAdapter = new EarthquakeAdapter(this, new ArrayList<Earthquake>());
// Set the adapter on the {#link ListView}
// so the list can be populated in the user interface
earthquakeListView.setAdapter(mAdapter);
// Set an item click listener on the ListView, which sends an intent to a web browser
// to open a website with more information about the selected earthquake.
earthquakeListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
// Find the current earthquake that was clicked on
Earthquake currentEarthquake = mAdapter.getItem(position);
// Convert the String URL into a URI object (to pass into the Intent constructor)
Uri earthquakeUri = Uri.parse(currentEarthquake.getUrl());
// Create a new intent to view the earthquake URI
Intent websiteIntent = new Intent(Intent.ACTION_VIEW, earthquakeUri);
// Send the intent to launch a new activity
startActivity(websiteIntent);
}
});
// EarthquakeAsyncTask task = new EarthquakeAsyncTask();
// task.execute(USGS_REQUEST_URL);
}
/**
* {#link AsyncTask} to perform the network request on a background thread, and then
* update the UI with the list of earthquakes in the response.
* <p>
* AsyncTask has three generic parameters: the input type, a type used for progress updates, and
* an output type. Our task will take a String URL, and return an Earthquake. We won't do
* progress updates, so the second generic is just Void.
* <p>
* We'll only override two of the methods of AsyncTask: doInBackground() and onPostExecute().
* The doInBackground() method runs on a background thread, so it can run long-running code
* (like network activity), without interfering with the responsiveness of the app.
* Then onPostExecute() is passed the result of doInBackground() method, but runs on the
* UI thread, so it can use the produced data to update the UI.
*/
private class EarthquakeAsyncTask extends AsyncTask<String, Void, List<Earthquake>> {
/**
* This method runs on a background thread and performs the network request.
* We should not update the UI from a background thread, so we return a list of
* {#link Earthquake}s as the result.
*/
#Override
protected List<Earthquake> doInBackground(String... urls) {
// Don't perform the request if there are no URLs, or the first URL is null.
if (urls.length < 1 || urls[0] == null) {
return null;
}
List<Earthquake> result = QueryUtils.fetchEarthquakeData(urls[0]);
return result;
}
/**
* This method runs on the main UI thread after the background work has been
* completed. This method receives as input, the return value from the doInBackground()
* method. First we clear out the adapter, to get rid of earthquake data from a previous
* query to USGS. Then we update the adapter with the new list of earthquakes,
* which will trigger the ListView to re-populate its list items.
*/
#Override
protected void onPostExecute(List<Earthquake> data) {
// Clear the adapter of previous earthquake data
mAdapter.clear();
// If there is a valid list of {#link Earthquake}s, then add them to the adapter's
// data set. This will trigger the ListView to update.
if (data != null && !data.isEmpty()) {
mAdapter.addAll(data);
}
}
}
}
I want this app to get data from the given URL whenever the user swipes down, but I am not able to find a way to do it. Can anyone please help me with the code that is to be written in the following code block:
final SwipeRefreshLayout pullToRefresh = findViewById(R.id.swipe_refresh);
pullToRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
pullToRefresh.setRefreshing(false);
}
});
call you methods which responsible for network call inside doRefresh method

What is the best way to refresh a list using Firebase

How I'm currently updating my lists in my project clearly isn't the standard for apps. How do would I refresh the list without clearing it?
This code writes just fine, without any interruptions; however, once the user has finished editing, the list is cleared and refreshed, returning the user to the top of the list. What is best practice when it comes to refreshing data, especially if the data is being edited by another user without interrupting the current user.
Writing:
protected void addStep() {
String stepID = Database.push().getKey();
step newStep = new step(recipeID, stepID, "stepImage", "","");
Database.child(stepID).setValue(newStep);
getData();
}
Adapter:
package asdasd.asd;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
/**
* This class is the class responsible for handling the lists of steps that the user will see during the creator phase.
* It works by setting an array adapter which uses the fragment_steps layout displaying it on a list within the StepActivity
* On text listeners are on each of the fields, when the user edits one of the fields, the program waits 600ms, and then uploads the
* data to the database;
* this refreshes the list TODO change the way the list refreshes in the instance of the creator; perhaps add a delay of 1 minuite before refresh, and or if a new step has been added
* A timer is responsible for this delay. The setting of data is to a specific path; being the recipe -> step -> long/shortText field.
*/
public class stepList extends ArrayAdapter<step>{
private Activity context;
private List<step> steps;
private DatabaseReference database;
private Timer timer1,timer2;
public stepList(Activity context, List<step> steps) {
super(context, R.layout.fragment_step, steps);
this.context = context;
this.steps = steps;
}
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//get database
database = FirebaseDatabase.getInstance().getReference("steps");
//init Layout inflater
LayoutInflater inflater = context.getLayoutInflater();
//step
View listViewItem = inflater.inflate(R.layout.fragment_step,null,true);
//step objects
final TextView descriptionText = (TextView) listViewItem.findViewById(R.id.stepRecipeShortText);
final TextView longDescriptionText = (TextView) listViewItem.findViewById(R.id.stepRecipeLongText);
ImageView stepImage = listViewItem.findViewById(R.id.stepImage);
//init step
final step step = steps.get(position);
//get stepID
final String stepID = step.getStepID();
//Set Data
descriptionText.setText(step.getStepDescription());
longDescriptionText.setText(step.getStepLongDescription());
//TODO If user has uploaded an image, then use that, else then use default
//Add listener to descriptionText so that when a user edits the fields, it is uploaded to the same step in the database
descriptionText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// user is typing: reset already started timer (if existing)
if (timer1 != null) {
timer1.cancel();
}
}
#Override
public void afterTextChanged(Editable s) {
timer1 = new Timer();
timer1.schedule(new TimerTask() {
#Override
public void run() {
String newDescriptionText = descriptionText.getText().toString();
addToDatabase(stepID,"stepDescription", newDescriptionText);
}
}, 600);
}
});
//Add listener to LongDescriptionText so that when a user edits the fields, it is uploaded to the same step in the database
longDescriptionText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// user is typing: reset already started timer (if existing)
if (timer2 != null) {
timer2.cancel();
}
}
#Override
public void afterTextChanged(Editable s) {
timer2 = new Timer();
timer2.schedule(new TimerTask() {
#Override
public void run() {
String newLongDescriptionText = longDescriptionText.getText().toString();
addToDatabase(stepID, "stepLongDescription", newLongDescriptionText);
}
}, 600);
}
});
return listViewItem;
}
//Add the data the user is entering to the database; there is a 600ms delay on the period between the user stopping typing and the data being updated.
private void addToDatabase(String id, String location, String text) {
database.child(id).child(location).setValue(text);
}
}
Getting:
public void getData() {
//receives all the recipes and adds them to the list
Database.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Clear the list
listStepsList.clear();
//Iterate through the nodes
for(DataSnapshot stepSnapshot : dataSnapshot.getChildren()){
//get recipe
step step = stepSnapshot.getValue(step.class);
//add step to list, if it is apart of the same recipe.
if(step.getRecipeID().equals(recipeID)) {
listStepsList.add(step);
}
}
//create Adapter
stepList stepAdapter = new stepList(StepActivity.this, listStepsList);
//Attatch adapter to listview
viewStepsList.setAdapter(stepAdapter);
stepAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I assume you're seeing a "big bang"/flash whenever there's a change in the database. If so, that is because you're updating the entire list, even if only a single item in the data was changed.
To improve this, you'll want to more granularly update the adapter for changes. To do so, you can attach a ChildEventListener, which fires events at one level lower in your data structure. So say a new node is added to your list, instead of rebuilding the entire list to add the one node, you'd get a single onChildAdded call with just the one new node. You'd then update listStepsList instead of rebuilding it, and tell the adapter about the changes.
For an example of this, I recommend checking out FirebaseUI, as the adapters in there all use this pattern. They build from a common FirebaseArray class that observes the database, and then have adapter classes to glue to array to the UI. For example, here's how FirebaseRecyclerAdapter connects the changes in the database to minimal updates to the view.

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!

Have at least one item selected in Android MultiSelectListPreference

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;
}
});

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.

Categories

Resources