I have different listview all same but now i have i problem. I want to create a method for saving favorite. Sorry for this simple question but I'm new on android and java. I know this data is static but I don't know is it possible make shared preference for saving this data.
import android.content.Intent;
import android.graphics.Color;
import android.renderscript.Sampler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.MobileAds;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ammunition extends AppCompatActivity {
String[] listaammunition = new String[]{"Arrow Zigzag","Arrow, Alchemist’s Fire(1)"};
EditText inputSearch;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ammunition);
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(getApplicationContext(),
android.R.layout.simple_list_item_1, listaammunition) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView text = (TextView) view.findViewById(android.R.id.text1);
text.setTextColor(Color.BLACK);
return view;
}
};
ListView listViewammunition = (ListView) findViewById(R.id.listViewammunition);
listViewammunition.setAdapter(adapter);
inputSearch = (EditText) findViewById(R.id.inputSearchmunizioni);
inputSearch.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) {
adapter.getFilter().filter(s);
}
#Override
public void afterTextChanged(Editable s) {
}
});
listViewammunition.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String Arrowalchemistsfire = adapter.getItem(position);
if (Arrowalchemistsfire.equals("Arrow, Alchemist’s Fire(1)")) {
Intent Arrowalchemistsfire1 = new Intent(getApplicationContext(), dettagli_armi.class);
Arrowalchemistsfire1.putExtra("costo", "75 gp");
Arrowalchemistsfire1.putExtra("dannis", "1D4");
Arrowalchemistsfire1.putExtra("dannim", "1D4");
Arrowalchemistsfire1.putExtra("crit", "x2");
Arrowalchemistsfire1.putExtra("rangeinc", "30 ft.");
Arrowalchemistsfire1.putExtra("weight", "1/5 lb.");
Arrowalchemistsfire1.putExtra("type", "fire");
Arrowalchemistsfire1.putExtra("dettagli", "Each of these projectiles carries a deadly load of alchemist's fire in its hollow shaft.\n" +
" When it strikes a target, the arrow's shaft shatters, releasing the alchemist's fire directly onto the target. One round after impact, the alchemist's fire ignites, dealing 1d4 points of damage.\n" +
"\n" +
" The target can use a full-round action to attempt to extinguish the flames before taking this damage. It takes a successful Reflex saving throw (DC 15) to extinguish the flames.\n" +
" Rolling on the ground earns the target a +2 bonus on the save. Submerging (such as by leaping into a lake) or magically extinguishing the flames automatically kills the flames.\n");
Arrowalchemistsfire1.putExtra("Source", "Dragon #349");
startActivity(Arrowalchemistsfire1);
}
String Arrowalchemistsfrost = adapter.getItem(position);
if (Arrowalchemistsfrost.equals("Arrow, Alchemist’s Frost(1)")) {
Intent Arrowalchemistsfrost1 = new Intent(getApplicationContext(), dettagli_armi.class);
Arrowalchemistsfrost1.putExtra("costo", "75 gp");
Arrowalchemistsfrost1.putExtra("dannis", "1D4");
Arrowalchemistsfrost1.putExtra("dannim", "1D4");
Arrowalchemistsfrost1.putExtra("crit", "x2");
Arrowalchemistsfrost1.putExtra("rangeinc", "30 ft.");
Arrowalchemistsfrost1.putExtra("weight", "1/5 lb.");
Arrowalchemistsfrost1.putExtra("type", "cold");
Arrowalchemistsfrost1.putExtra("dettagli", "Similar in designed to the Arrow alchemist's fire, this arrow instead carrier an alchemical compound that creates a spray of intense cold.\n" +
" If the arrow strikes a target, the arrow immediately shatters and deals 1d4 points of cold damage.\n");
Arrowalchemistsfrost1.putExtra("Source", "Dragon #349");
startActivity(Arrowalchemistsfrost1);
}
});
}
}
Use tinyDB , you need yo add just one class.
https://github.com/kcochibili/TinyDB--Android-Shared-Preferences-Turbo
Here is code snippet
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ecart);
//Fetch data from DB
GlobaDataHolder.getGlobaDataHolder().setShoppingList(
new TinyDB(getApplicationContext()).getListObject(
PreferenceHelper.MY_CART_LIST_LOCAL, Product.class));
}
#Override
protected void onPause() {
super.onPause();
// Store Shopping Cart in DB
new TinyDB(getApplicationContext()).putListObject(
PreferenceHelper.MY_CART_LIST_LOCAL, GlobaDataHolder
.getGlobaDataHolder().getShoppingList());
}
For complete code refer
https://github.com/hiteshsahu/ECommerce-App-Android/blob/master/app/src/main/java/com/hitesh_sahu/retailapp/view/activities/ECartHomeActivity.java
Related
I have an activity with two variables
int cifracero
and
int cifrauno
I want to pass those variables to a fragment to be shown in some TextViews as the result of the multiplication made in the previous screen...like a "Congratulations, the multiplication was..."
I need to do something like a bundle to pass the variables as arguments of the second screen, in this case a fragment.
And then I have a third variable resultado which is the result of the multiplication of cifracero and cifrauno
This is the activity code
package com.example.aprendelastablasdemultiplicar;
import androidx.activity.ComponentActivity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.ListFragment;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import org.w3c.dom.Text;
public class quizingresar extends ComponentActivity {
private EditText cifracero;
private EditText cifrauno;
private TextView resultado;
private Button calcular;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quizingresar);
cifracero = (EditText) findViewById(R.id.cifracero);
cifrauno = (EditText) findViewById(R.id.cifrauno);
resultado = (TextView) findViewById(R.id.resultado);
calcular = (Button)findViewById(R.id.calcular);
TextWatcher textWatcher = 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) {
int multiplicationResult = multiplicarValores();
resultado.setText(Integer.toString(multiplicationResult));
}
#Override
public void afterTextChanged(Editable s) {
}
};
cifracero.addTextChangedListener(textWatcher);
cifrauno.addTextChangedListener(textWatcher);
calcular.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
openCongratulationsCalcular();
}
});
}
private int multiplicarValores() {
final String strnumber0 = cifracero.getText().toString();
final String strnumber1 = cifrauno.getText().toString();
int number0 = 0;
int number1 = 0;
try {
number0 = Integer.parseInt(strnumber0);
} catch (NumberFormatException e) {
}
try {
number1 = Integer.parseInt(strnumber1);
} catch (NumberFormatException e) {
}
return number0 * number1;
}
public void openCongratulationsCalcular(){
Intent intent = new Intent(this, congratulationscalcular.class);
startActivity(intent);
}
}
I need to recover the variables from the previous screen to be shown in the new fragment screen
You can have both of your fragments share the data between them by having them both under the same viewmodel, as shown here.
If you are going to be swapping screens, I think a very efficient solution would be to pass the desired data from one (screen) fragment to another using attributes in your Navigation Graph. Shown here.
Take a look at Data-Binding as well if you'd like, might be useful.
Intent intent = new Intent(this, DisplayMessageActivity.class);
String result = resultado.getText().toString();
intent.putExtra("EXTRA_MESSAGE", result);
startActivity(intent);
to get the message that was passed by the first activity
Intent intent = getIntent();
String message = intent.getStringExtra("EXTRA_MESSAGE");
I have been working on an activity which has two edittext fields and two spinners. The text entered in the EditText fields doesn't show up in the toast that I generate, even though the selections done in the spinners does. What am I doing wrong ?
package com.goswami.pntgo.notifierdemo;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.*;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
public class LoginScreen extends Activity {
String Course = null;
String Semester = null;
String univRollNo = null;
String name = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login_screen);
final EditText tvname = (EditText) findViewById(R.id.name);
final EditText tvunivrn = (EditText) findViewById(R.id.univ_roll_no);
name = tvname.getText().toString();
univRollNo = tvunivrn.getText().toString();
Spinner course = (Spinner)findViewById(R.id.course);
Spinner semester = (Spinner)findViewById(R.id.semester);
ArrayAdapter cadapter = ArrayAdapter.createFromResource(this,R.array.courses,R.layout.courses);
ArrayAdapter sadapter = ArrayAdapter.createFromResource(this,R.array.semesters,R.layout.courses);
course.setAdapter(cadapter);
semester.setAdapter(sadapter);
course.setOnItemSelectedListener(new OnItemSelectedListener(){
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id){
Course = parent.getItemAtPosition(pos).toString();
}
});
semester.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Semester = parent.getItemAtPosition(position).toString();
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
Button login = (Button)findViewById(R.id.button);
login.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
Toast.makeText(LoginScreen.this,(name+" Roll No:"+univRollNo+"\n"+Course+" "+Semester),Toast.LENGTH_LONG).show();
}
});
}
}
You're setting the contents of univRollNo before anything has been entered into tvunivrn.
Add this line to the top of your login button OnClickListener:
univRollNo = tvunivrn.getText().toString();
Or just circumvent that and extract the text as such:
Toast.makeText(LoginScreen.this, name + " Roll No:" + tvunivrn.getText().toString() + "\n" + Course + " " + Semester, Toast.LENGTH_LONG).show();
I'm making an app in which I'm using Spannble class to create a highlighter mechanism in an EditText, which works fine. Now, I want to store the span position(s) in a SQLiteDatabase, so that I can reload them and have the Spanned Text to show it in a TextView.
Here's my TextView alongwith the highlighter mechanism -
package com.Swap.RR;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Typeface;
import android.text.Editable;
import android.text.Spannable;
import android.text.TextWatcher;
import android.text.style.BackgroundColorSpan;
import android.view.Menu;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ToggleButton;
public class Add_New_Note extends Activity {
private EditText note;
private int mStart;
private Typeface Roboto_lt;
private int end;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add__new__note);
note = (EditText) findViewById(R.id.notecontent);
ToggleButton highlighter = (ToggleButton) findViewById(R.id.highlightToggle);
Roboto_lt = Typeface.createFromAsset(getAssets(), "Roboto-Light.ttf");
note.setTypeface(Roboto_lt);
mStart = -1;
final TextWatcher highlightWatcher = new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count)
{
if(mStart > 0)
{
end = note.getText().length();
note.getText().setSpan(new BackgroundColorSpan(getResources().getColor(R.color.highlighter_green)), mStart, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
public void afterTextChanged(Editable s) {
}
};
highlighter.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton button, boolean isChecked) {
if (isChecked == true) {
//start highlighting when the ToggleButton is ON
if(mStart == -1) mStart = note.getText().length();
else mStart = -1;
mStart = note.getText().length();
note.addTextChangedListener(highlightWatcher);
} else {
//stop highlighting when the ToggleButton is OFF
note.removeTextChangedListener(highlightWatcher);
}
}
});
}
public void noteDone(View v) {
String noteContent = note.getText().toString();
Intent i = new Intent("com.Swap.Notes_page");
i.putExtra("NOTE", noteContent);
setResult(RESULT_OK, i);
finish();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_add__new__note, menu);
return true;
}
}
Please help me with some suggestions on how to do it. Thanks!
Your best bet is to use Html.toHtml() to convert the Spannable into HTML. You can then use Html.fromHtml() to convert the HTML back into a Spannable when you query the database later on. You will want to do adequate testing, though, as toHtml() and fromHtml() are not guaranteed to support the same HTML tags.
I am googling for the last few hours, I've seen this question asked like 20 times but none of the solutions seem to work for me. I have a ListView and a CustomList class, that incorporates a list adapter and a simple dialog for editing/removing/renaming items. everything works as supposed until the first screen rotation. After that, the list stops being updated, although the adapter contains all the right data.
I will post both files without skipping a line. I don't need any data being saved at this time, I will handle that later. This is really weird because supposedly the application gets completely restarted after screen rotation. What makes it even stranger, the dummy items are added onCreate even after screen rotation, but after that there is no other response from the list.
Please excuse my english and my noobness and try to point me in the right direction.
package com.sl.mylandmarks;
import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.location.LocationManager; import android.os.Bundle; import android.provider.Settings; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView;
public class MainActivity extends Activity {
Button addButton; ListView landmarksList; EditText inputName, inputSearch; CustomList customList; Context globalContext; TextView gpsState; private LocationManager locationManager; private GPSTracker gpsTracker; private double longitude; private double latitude;
#Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
gpsTracker = new GPSTracker(MainActivity.this);
globalContext = this;
addButton = (Button) findViewById(R.id.addBtn);
customList = new CustomList(this);
landmarksList = (ListView) findViewById(R.id.listView1); landmarksList.setAdapter(customList.getAdapter()); landmarksList.setChoiceMode(ListView.CHOICE_MODE_SINGLE); landmarksList.setTextFilterEnabled(true); landmarksList.setLongClickable(true); customList.theList = landmarksList;
inputName = (EditText) findViewById(R.id.EditItemName);
gpsState = (TextView) findViewById(R.id.gpsState);
ContentResolver contentResolver = getBaseContext().getContentResolver(); boolean gpsStatus = Settings.Secure.isLocationProviderEnabled(
contentResolver, LocationManager.GPS_PROVIDER); if (gpsStatus) { gpsState.setText("GPS Enabled"); } else { gpsState.setText("GPS Disabled"); }
// // SEARCH BOX
inputSearch = (EditText) findViewById(R.id.editText3); inputSearch.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) { }
public void onTextChanged(CharSequence s, int start, int before,
int count) {
customList.getAdapter().getFilter().filter(s); } }); // // SEARCH BOX
// / DUMMY ITEMS customList.addItem("Home", "43.1565 / 15.8645"); customList.addItem("Work", "43.1565 / 15.8645"); customList.addItem("Denis` apartment", "43.1565 / 15.8645"); customList.addItem("Natasa", "43.1565 / 15.8645"); customList.addItem("Bruce Wayne", "43.1565 / 15.8645"); customList.addItem("Walker shop", "43.1565 / 15.8645"); customList.addItem("Chuck Norris Residence", "43.1565 / 15.8645"); customList.addItem("Some landmark", "43.1565 / 15.8645"); // customList.removeItem(3);
OnItemLongClickListener listLongClick = new OnItemLongClickListener() {
#Override public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
customList.showOptionsDialog(arg2);
return true;// event consumed, not dispatched forward } };
landmarksList.setOnItemLongClickListener(listLongClick);
OnClickListener ButtonClick = new View.OnClickListener() {
#Override public void onClick(View v) {
switch (v.getId()) {
case R.id.addBtn:
customList.addItem(inputName.getText().toString(),
"45.5644 / 23.6541");
inputName.setText("");
break;
}
}
};
addButton.setOnClickListener(ButtonClick);
}
#Override public void onPause() { super.onPause();
}
#Override public void onResume() { super.onResume();
}
#Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true;
}
}
and CustomList.java
package com.sl.mylandmarks;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Dialog;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleAdapter;
public class CustomList {
ArrayList<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>(
2);
HashMap<String, String> maplist;
SimpleAdapter listAdapter;
public Context context;
public ListView theList;
public CustomList(Context con) {
context = con;
String[] from = { "line1", "line2" };
int[] to = { android.R.id.text1, android.R.id.text2 };
listAdapter = new SimpleAdapter(context, list,
android.R.layout.simple_list_item_2, from, to);
}
public void addItem(String name, String coords) {
if ((name != null) && (name.length() != 0)) {
maplist = new HashMap<String, String>();
maplist.put("line1", name);
maplist.put("line2", coords);
list.add(maplist);
listAdapter.notifyDataSetChanged();
}
}
public void removeItem(int id) {
Log.d("removing",list.remove(id).toString());
listAdapter.notifyDataSetChanged();
}
public void renameItem(int id, String newName) {
Log.d("SL","Rename Selected");
maplist = new HashMap<String, String>();
maplist.put("line1", newName);
maplist.put("line2", list.get(id).get("line2"));
list.set(id, maplist);
listAdapter.notifyDataSetChanged();
}
public SimpleAdapter getAdapter() {
return listAdapter;
}
public void showOptionsDialog(final int position) {
//Log.d("SL", String.valueOf(position));
final Dialog optionsDialog = new Dialog(context);
optionsDialog.setContentView(R.layout.list_dialog);
optionsDialog.setTitle("Options");
optionsDialog.show();
final EditText itemNameEdit = (EditText) optionsDialog
.findViewById(R.id.EditItemName);
final Button removeBtn = (Button) optionsDialog
.findViewById(R.id.removeBtn);
final Button renameBtn = (Button) optionsDialog
.findViewById(R.id.renameBtn);
final Button cancelBtn = (Button) optionsDialog
.findViewById(R.id.cancelBtn);
itemNameEdit.setText(list.get(position).get("line1"));
itemNameEdit.setSelection(itemNameEdit.length());
OnClickListener ButtonClick = new View.OnClickListener() {
#Override
public void onClick(View arg0) {
switch (arg0.getId()) {
case R.id.removeBtn:
removeItem(position);
optionsDialog.dismiss();
break;
case R.id.renameBtn:
renameItem(position, itemNameEdit.getText().toString());
optionsDialog.dismiss();
break;
case R.id.cancelBtn:
optionsDialog.dismiss();
break;
}
}
};
removeBtn.setOnClickListener(ButtonClick);
renameBtn.setOnClickListener(ButtonClick);
cancelBtn.setOnClickListener(ButtonClick);
}
}
I not sure what exactly is your question. And I didn't look at your code, because I don't think that SO is a good place to just dump classes and say "fix this". But I might have an answer for you.
It is true that Android is recreating the activity when you rotate (like roky says). If you come from an iPhone development background (which is my case), it look like kind of weird.
But if your activity is not too complex, you can say to Android to handle it. Do to this, add this to your tag of your AndroidManifest.xml
<activity android:configChanges="orientation|screenSize" ... >
</activity>
Here is a better explanation than mine : android:configChanges="orientation" does not work with fragments
If my answer was the good one, it would be nice to accept it!
I have 3 classes, I wish to use the autocomplete text box to show user certain data (aka cities) from a web service (rest api). I've used this implementation on various features of my own application, but for some reason, there's a synchronization problem within the textchangedlistener...
CitiesArrayAdapter.java (to show a different view, in my case the "city, state"):
package com.android.lzgo.adapters;
import java.util.ArrayList;
import java.util.List;
import com.android.lzgo.activities.LiftSearchActivity;
import com.android.lzgo.activities.R;
import com.android.lzgo.models.City;
import com.android.lzgo.models.Lift;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class CitiesArrayAdapter extends ArrayAdapter<City> {
private static final String TAG = CitiesArrayAdapter.class.getName();
private final ArrayList<City> cities;
private int viewResourceId;
public CitiesArrayAdapter(Context context, int textViewResourceId, ArrayList<City> results) {
super(context, textViewResourceId, results);
this.cities = results;
this.viewResourceId = textViewResourceId;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// assign the view we are converting to a local variable
View v = convertView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(viewResourceId, null);
}
City i = cities.get(position);
Log.d(TAG, "Here is my value: " + i);
if (i != null) {
TextView tt = (TextView) v.findViewById(android.R.id.text1);
Log.d(TAG, "Name: " + i.getName() + ", " + i.getProvince_name());
if (tt != null){
tt.setText("Name: " + i.getName() + ", " + i.getProvince_name());
}
}
// the view must be returned to our activity
return v;
}
}
CitiesResponderFragment.java (this is how I get my values from my rest api):
package com.android.lzgo.fragment;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import com.android.lzgo.activities.LiftSearchActivity;
import com.android.lzgo.definitions.Constants;
import com.android.lzgo.models.City;
import com.android.lzgo.service.LzgoService;
import com.google.gson.Gson;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.Toast;
public class CitiesResponderFragment extends LzgoResponderFragment {
private static String TAG = CitiesResponderFragment.class.getName();
private List<City> mCities;
ArrayAdapter<City> adapter;
private String enteredCharacters;
LiftSearchActivity activity;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
activity = (LiftSearchActivity) getActivity();
// This gets called each time our Activity has finished creating itself.
getCities();
}
private void getCities() {
if (mCities == null && activity != null) {
Intent intent = new Intent(activity, LzgoService.class);
intent.setData(Uri.parse(Constants.REST_CITIES_AUTOCOMPLETE));
Bundle params = new Bundle();
params.putString("search", getenteredCharacters());
intent.putExtra(LzgoService.EXTRA_HTTP_VERB, LzgoService.GET);
intent.putExtra(LzgoService.EXTRA_PARAMS, params);
intent.putExtra(LzgoService.EXTRA_RESULT_RECEIVER, getResultReceiver());
// Here we send our Intent to our RESTService.
activity.startService(intent);
}
}
#Override
public void onRESTResult(int code, String result) {
Log.e(TAG, Integer.toString(code));
Log.e(TAG, result);
// Check to see if we got an HTTP 200 code and have some data.
if (code == 200 && result != null) {
mCities = getCitiessFromJson(result);
adapter = activity.getArrayAdapter();
adapter.clear();
for( City city : mCities){
//debugging
Log.d(TAG, "City : " + city.getName());
adapter.add(city);
adapter.notifyDataSetChanged();
}
getCities();
}
else {
Activity activity = getActivity();
if (activity != null && code == 400) {
Toast.makeText(activity, result, Toast.LENGTH_SHORT).show();
}
else
Toast.makeText(activity, "Failed to load lzgo data. Check your internet settings.", Toast.LENGTH_SHORT).show();
}
}
private List<City> getCitiessFromJson(String json) {
ArrayList<City> cityList = new ArrayList<City>();
Gson gson = new Gson();
try {
JSONObject citiesWrapper = (JSONObject) new JSONTokener(json).nextValue();
JSONArray cities = citiesWrapper.getJSONArray("cities");
for (int i = 0; i < cities.length(); i++) {
//JSONObject city = cities.getJSONObject(i);
String jsonCity = cities.getString(i);
City city = gson.fromJson( jsonCity, City.class );
//Log.e(TAG, "Hurray! Parsed json:" + city.getString("name"));
//cityList.add(city.getString("name"));
cityList.add(city);
}
}
catch (JSONException e) {
Log.e(TAG, "Failed to parse JSON.", e);
}
return cityList;
}
public String getenteredCharacters() {
return enteredCharacters;
}
public void setenteredCharacters(String characters) {
this.enteredCharacters = characters;
}
}
LiftSearchActivity.java (My FragmentActivity):
package com.android.lzgo.activities;
import java.util.ArrayList;
import com.android.lzgo.adapters.CitiesArrayAdapter;
import com.android.lzgo.fragment.CitiesResponderFragment;
import com.android.lzgo.models.City;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.AutoCompleteTextView;
import android.widget.DatePicker;
public class LiftSearchActivity extends FragmentActivity{
private static final String TAG = LiftSearchActivity.class.getName();
// User lift input
private AutoCompleteTextView autoCityFrom;
private AutoCompleteTextView autoCityTo;
private DatePicker date;
private CitiesArrayAdapter adapter;
private ArrayList<City> mCities ;
int year , month , day;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.lift_search);
mCities = new ArrayList<City>();
adapter = new CitiesArrayAdapter(this,
android.R.layout.simple_dropdown_item_1line, mCities);
autoCityFrom = (AutoCompleteTextView) findViewById(R.id.cityFrom);
autoCityTo = (AutoCompleteTextView) findViewById(R.id.cityTo);
adapter.setNotifyOnChange(true);
autoCityFrom.setAdapter(adapter);
autoCityTo.setAdapter(adapter);
autoCityFrom.addTextChangedListener(new TextWatcher() {
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
// no need to do anything
}
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (((AutoCompleteTextView) autoCityFrom).isPerformingCompletion()) {
return;
}
if (charSequence.length() < 2) {
return;
}
String query = charSequence.toString();
getCities(query);
}
public void afterTextChanged(Editable editable) {
}
});
autoCityTo.addTextChangedListener(new TextWatcher() {
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
// no need to do anything
}
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (((AutoCompleteTextView) autoCityTo).isPerformingCompletion()) {
return;
}
if (charSequence.length() < 2) {
return;
}
String query = charSequence.toString();
getCities(query);
}
public void afterTextChanged(Editable editable) {
}
});
date = (DatePicker) findViewById(R.id.dpResult);
}
public void searchLifts(View view) {
Intent intent = new Intent(this, LiftsResultActivity.class);
//While autocomplete doesn't work hardcore value...
intent.putExtra("from", Integer.toString(9357)); // Sherbrooke
intent.putExtra("to", Integer.toString(6193)); // Montreal
intent.putExtra("date", Integer.toString(date.getMonth()+1) + "-" + Integer.toString(date.getDayOfMonth()) + "-" + Integer.toString(date.getYear()));
startActivity(intent);
}
public void getCities(String query) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
CitiesResponderFragment responder = (CitiesResponderFragment) fm.findFragmentByTag("RESTResponder");
responder = new CitiesResponderFragment();
responder.setenteredCharacters(query);
ft.add(responder, "RESTResponder");
ft.commit();
}
public CitiesArrayAdapter getArrayAdapter() {
// TODO Auto-generated method stub
return adapter;
}
}
I get the correct result and all. But my service doesn't seem to populate my array adapter in my activity, when I try to show my first "city", my adapter contains nothing. I wonder if I have to put a notifydatasetchanged (I tried, but doesn't work). I'm kind of confuse... any pointers?
While debugging the application I noticed that the properties mObjects of
the ArrayAdapter is cleared even if the associated ArrayList has
elements, and then properties mOriginalValues is filled with the
Strings loaded the first time.
Without seeing the full code base(+ data), I don't know if someone can pin point the reason for your code's failure. But I think the problem comes more from the way you setup the whole auto complete related code than some obvious line error. Below I'll try to address some of this issues(from my point of view) :
First of all, in the TextWatcher from LiftSearchActivity you call the getCities() method which adds, each time the user modifies the auto complete text, a Fragment to the Activity. I really doubt you want this, you should probably look at having only one Fragment in the Activity on which to call a refresh(or update) method, passing it the new filter text. If the user rotates the phone those fragments will also be recreated because of the configuration change.
Second, in the CitiesResponderFragment class you call the fragment's getCities() method(if you don't have any data) in onActivityCreated which start an update service(?!). Now related to the first point, you could end up doing a lot of unnecessary queries, for example if the user enter 4 characters and then decides to delete one of the characters as it was incorrect you'll end up with 5 added fragments, from which 3 will make the service start/query for data.
And last, I'm not sure if you understand how the AutoCompleteTextView works under the hood. In order to provide the drop down with the suggestions the AutoCompleteTextView widget will filter its adapter(adapter.getFilter()) and show as suggestions the items that match the filter. I don't know if you set some threshold on the AutoCompleteTextView but initially the auto complete will be empty for the first 3 characters entered as you start with an empty list of items when you first setup the Activity. The first two characters will not show anything because you start with an empty list and you don't add any new fragments(charSequence.length() < 2). The third character will most likely also not show anything, because the overhead of creating the fragment, starting the service and fetching the data will almost for sure be greater then doing the work of the adapter filtering(which will still see the initially empty list). I don't know if you tested, but from this fourth character the adapter should have some elements in it and the filtering should show something. Clearing the adapter and adding new data in it willl only make that data available to the next character entered in the AutoCompleteTextView.
The proper way of doing the filtering would be to further extend your adapter and implement the getFilter() method to return your own Filter implementation which would query the data store for new filtered items. The filtering method runs on a background thread, with a little work I think you could implement your current logic with the Service and the REST callback.
See example of the REST API Autocomplete : https://subversion.assembla.com/svn/rockitsearch-android/
Although it is part of my service, it can also serve as an example of how you can integrate your own REST API in AutoCompleteTextView control. If you are interested in autocomplete 3rd party service, here you go : www.rockitsearch.com