How can I convert ListView population from JSON to SharedPreferences? - android

I have a listview in a fragment that is populated with a list of events. I have a heart icon on these list items that allows the user to click and favorite the items. I am currently saving the event id in sharedprefs when the item is Favorited.
This is working properly, and saves the favorited item across user sessions. I now need to populate these favorited items in the favorites listview. The old developer tried a JSON implementation from an api, but it never worked. I now just want to populate this list from the sharedprefs. Heres my favorites fragment code...
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.TextView;
import com.appsflyer.AppsFlyerLib;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import lakeviewlabs.ticketliquidator.api.delete_favorites.DeleteFavoritesApi;
import lakeviewlabs.ticketliquidator.api.delete_favorites.DeleteFavoritesApiOut;
import lakeviewlabs.ticketliquidator.api.get_favorites.GetFavoritesApi;
import lakeviewlabs.ticketliquidator.api.get_favorites.GetFavoritesApiOut;
import static com.urbanairship.UAirship.getApplicationContext;
public class FavoritesFragment extends Fragment implements GetFavoritesApiOut, DeleteFavoritesApiOut {
View rootView;
ProgressDialog pDialog;
FavoritesAdapter adapter;
Boolean checkState = false;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Map<String, Object> eventValue = new HashMap<String, Object>();
AppsFlyerLib.getInstance().trackEvent(getActivity().getApplicationContext(), "View Favorites",eventValue);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
Map<String,?> keys = prefs.getAll();
for(Map.Entry<String,?> entry : keys.entrySet()){
Log.d("map values",entry.getKey() + ": " +
entry.getValue());
if (entry.getKey().matches("[0-9]+") && entry.getValue().equals(true)) {
Log.d("using values",entry.getKey() + ": " +
entry.getValue());
// performersList.add(entry.getKey().);
}
}
rootView = inflater.inflate(R.layout.fragment_favorites, container, false);
GetFavoritesApi api = new GetFavoritesApi(getContext(), this);
rootView.findViewById(R.id.delete_favorites).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
deleteFavorites();
}
});
showLoadingMessage();
api.get();
return rootView;
}
private void deleteFavorites() {
ArrayList<String> ids = new ArrayList<String>();
ListView listView = (ListView) rootView.findViewById(R.id.favorites);
DeleteFavoritesApi api = new DeleteFavoritesApi(getContext(), this);
for (int i = 0; i < listView.getCount(); i++){
View view = listView.getChildAt(i);
CheckBox check = (CheckBox)view.findViewById(R.id.favorite_select);
if(check.isChecked()){
ids.add(view.getTag().toString());
}
}
if(ids.isEmpty()){
onDeleteFavoritesSuccess();
return;
}
api.delete(android.text.TextUtils.join(",", ids));
}
private void showLoadingMessage(){
pDialog = new ProgressDialog(getContext());
pDialog.setMessage("Getting favorite performers...");
pDialog.show();
}
#Override
public void onGetFavoritesSuccess(JSONArray favorites) {
ListView listView = (ListView) rootView.findViewById(R.id.favorites);
ArrayList<JSONObject> performersList = new ArrayList<JSONObject>();
for(int i = 0; i < favorites.length(); i++) {
JSONObject performer = null;
try {
performer = favorites.getJSONObject(i);
performersList.add(performer);
} catch (JSONException e) {
e.printStackTrace();
}
}
adapter = new FavoritesAdapter(getContext(), performersList);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new ListView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if(checkState) {
CheckBox check = (CheckBox)view.findViewById(R.id.favorite_select);
check.setChecked(!check.isChecked());
}else{
Intent intent = new Intent(getContext(), PerformerActivity.class);
intent.putExtra("performer_id", view.getTag().toString());
startActivity(intent);
}
}
});
pDialog.dismiss();
}
#Override
public void onGetFavoritesError(JSONObject errors) {
}
#Override
public void onGetFavoritesUnexpectedError() {
}
public void showCheckboxes() {
checkState = !checkState;
rootView.findViewById(R.id.delete_favorites).setVisibility(checkState ? View.VISIBLE : View.INVISIBLE);
ListView listView = (ListView) rootView.findViewById(R.id.favorites);
for (int i = 0; i < listView.getCount(); i++){
View view = listView.getChildAt(i);
CheckBox check = (CheckBox)view.findViewById(R.id.favorite_select);
check.setVisibility(checkState ? View.VISIBLE : View.GONE);
check.setChecked(false);
// view.animate()
// .translationX(150)
// .setDuration(300)
// .setListener(new AnimatorListenerAdapter() {
// #Override
// public void onAnimationEnd(Animator animation) {
//
// }
// });
}
//
}
#Override
public void onDeleteFavoritesSuccess() {
GetFavoritesApi api = new GetFavoritesApi(getContext(), this);
showLoadingMessage();
showCheckboxes();
api.get();
}
}
I've tried parsing all the sharedprefs and Im able to log the ones that are needed. I just cant get them to display in the favorites list.

You can store the object of the item as JSON string in the Shared Preferences and whenever new item is added to favorites append the JSON object for that item to the JSON string and in the Favorites list read and parse the json string from Shared Preferences. I'm assuming you get the results in JSON array from the API. I would suggest store the JSON string in the same format as received so that you can reuse the same POJOs for favorites list.
Hope this helps.

You should not store your preferences like that. Storing your event using their ids as a key creates a big mess in your Shared Preferences.
Instead:
Store a list of event id. As you can't store a list in the Shared Preferences, use Gson to convert your list into a Json string. Then store the string. Use Gson again to regenerate the list from the stored string.
The other (better) option, is to use a local database. It's gonna be easier, and cleaner. You will be able to store more information easily. Like that you can store the entire event (and not the id only) and request them back to populate your ListView of favourite events
Note: You could store the entire events in the Shared Preferences as suggested by #Varun_Ramani but it's not recommended as the purpose of the Shared Preferences is not to replace databases (as explained in the official documentation, see below).
If you have a relatively small collection of key-values that you'd like to save, you should use the SharedPreferences APIs.

Related

Data didnt load, and not error or warning display

i got trouble, im try convert my fragment into activity, and when im try running the apps, all data not load and no warning or error shown, pls someone help
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.NetworkImageView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class RekomendasiExercise extends AppCompatActivity implements AdapterView.OnItemClickListener{
// Log tag
private static final String TAG = AbdominalFragment.class.getSimpleName();
// Movies json url
private static final String url = "http://......";
private ProgressDialog pDialog;
private List<Exercise> exerciseList = new ArrayList<Exercise>();
private ListView listView;
private CustomListAdapter adapter;
public RekomendasiExercise() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onDestroy() {
super.onDestroy();
hidePDialog();
}
private void hidePDialog() {
if (pDialog != null) {
pDialog.dismiss();
pDialog = null;
}
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activity_list, container, false);
// Inflate the layout for this fragment
final ListView listView = (ListView) rootView.findViewById(R.id.list);
adapter = new CustomListAdapter(this, exerciseList);
listView.setAdapter(adapter);
pDialog = new ProgressDialog(this);
// Showing progress dialog before making http request
pDialog.setMessage("Loading...");
pDialog.show();
if(exerciseList.isEmpty()) {
// Creating volley request obj
JsonArrayRequest exerciseReq = new JsonArrayRequest(url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.d(TAG, response.toString());
hidePDialog();
exerciseList.clear();
// Parsing json
for (int i = 0; i < response.length(); i++) {
try {
JSONObject obj = response.getJSONObject(i);
Exercise exercise = new Exercise();
if (obj.getString("tipe").equals("abdominal")) {
exercise.setNama(obj.getString("nama"));
exercise.setGambar1(obj.getString("gambar1"));
// Genre is json array
// adding movie to movies array
exerciseList.add(exercise);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
// notifying list adapter about data changes
// so that it renders the list view with updated data
adapter.notifyDataSetChanged();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(TAG, "Error: " + error.getMessage());
hidePDialog();
}
});
// Adding request to request queue
AppController.getInstance().addToRequestQueue(exerciseReq);
}else{
hidePDialog();
}
listView.setOnItemClickListener(this);
return rootView;
}
#Override
public void onItemClick(AdapterView<?> parent, View convertView, int position, long id) {
// Create custom dialog object
final Dialog dialog = new Dialog(this);
// Include dialog.xml file
dialog.setContentView(R.layout.dialog); // layout of your dialog
ImageLoader imageLoader = AppController.getInstance().getImageLoader();
NetworkImageView thumbnail1 = (NetworkImageView ) dialog.findViewById(R.id.gambar1);
thumbnail1.setImageUrl(exerciseList.get(position).getGambar1(), imageLoader);
// Set dialog title
dialog.setTitle("Detail");
// set values for custom dialog components - text, image and button
TextView nama = (TextView) dialog.findViewById(R.id.nama);
nama.setText("Nama = " + exerciseList.get(position).getNama());
dialog.show();
}
}
if default in Fragment its running perfectly and no problem, but when im try using activity all data not load.
I can't see any code that would run when the activity is created. You have an onCreateView method (which I am guessing you are carrying over from your Fragment based implementation). Unless somewhere in your code (which I cannot see how) you are calling that method, it never gets called since it is not part of the Activity lifecycle. I would copy over the entire code from that method to onCreate method (as a starting point) and delete the method. I would then make the following changes to the first few lines
// View rootView = inflater.inflate(R.layout.activity_list, container, false);
// Noticed that I commented out the above line and changed the line below
final ListView listView = (ListView) findViewById(R.id.list);
I am not suggesting that this will solve all of your problems as there might be (likely) other issues, but you should potentially be the first step. I strongly suggest you get more familiar with the Activity and Fragment classes and become more comfortable with their lifecycles. Also, you should get rid of
public RekomendasiExercise() {
// Required empty public constructor
}
Best of luck

Listview setonitemclicklistener

I've looked up similar post regarding this question but they didn't solve my issue, so here it is.
I'm trying to set a setOnItemClickListener, for when I click on any item of my list I will open a dialog with the info (retrieve from the list) on the clicked list and more info. But i'm struggling to implement the setOnItemClickListener part.
I'm getting and error in line: myList.setOnItemClickListener(new OnItemClickListener()){
package com.example.proyectoprueba;
import java.util.ArrayList;
import java.util.HashMap;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;;
public class PlantillaChina extends ActionBarActivity{
// DB Class to perform DB related operations
DBController controller = new DBController(this);
// Progress Dialog Object
ProgressDialog prgDialog;
HashMap<String, String> queryValues;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.lista);
Intent myIntent = getIntent();
String value = myIntent.getStringExtra("key");
TextView titulo=(TextView)findViewById(R.id.titulo);
// Get Platos records from SQLite DB
ArrayList<HashMap<String, String>> platoList = controller.getAllEntremeses();
if (value.equals("entremeses")){
platoList = controller.getAllEntremeses();
titulo.setText(value);
}else if(value.equals("arroces y pasta")){
platoList = controller.getAllArrocesyPasta();
titulo.setText(value);
}else if(value.equals("mar")){
platoList = controller.getAllMar();
titulo.setText(value);
}else if(value.equals("carnes")){
platoList = controller.getAllCarnes();
titulo.setText(value);
}
// If Platos exists in SQLite DB
if (platoList.size() != 0) {
// Set the Plato Array list in ListView
ListAdapter adapter = new SimpleAdapter(PlantillaChina.this, platoList, R.layout.itemlista, new String[] {
"platoId", "platoNombre", "platoDescripcion", "platoPrecio" }, new int[] {R.id.codigo, R.id.nombre, R.id.descripcion, R.id.precio });
ListView myList = (ListView) findViewById(R.id.listaplatos);
myList.setAdapter(adapter);
myList.setOnItemClickListener(new OnItemClickListener()){
}
}
Thank you for your time
It think you want something like this.
myList.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int
position, long id)
{
// You have the position available here as well as the clicked view.
}
});
You need to actual define the OntItemClickListener to do something.
First, the code you have doesn't make much sense. You want something like this:
myList.setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3)
{
// Your code here
}
});
Basically you are creating your own OnItemClickListener class, and overriding the OnItemClick method. This callback will get called whenever an item in your list is clicked. You can do whatever processing you would like inside that function.

How can I delete an item from an online database via android application?

I started to develop Android recently and I'm having difficulty in solving a thing probably much simpler than what I think.
I am creating a CRUD application that communicates with an online database, I can read and enter data, unfortunately I can't delete and modify the list once it is created.
To manage the data that use JSON and the row in the list that I add is made up of three fields: animal_id, animal_name, animal_type.
The activity data on which I read and on which I want to implement methods to modify and delete via listener is formed by the following code:
import java.sql.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
public class ListAnimalActivity extends ListActivity {
ArrayList<HashMap<String,?>> animalList;
JSONParser jParser = new JSONParser();
JSONArray animals = null;
Button button_add;
private static String url_read = "http://example.com/list_animals.php";
private static String url_delete = "http://example.com/delete_animal.php";
private static final String TAG_SUCCESS = "success";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
load_ListAnimalActivity();
}
public void onResume(){
super.onResume();
load_ListAnimalActivity();
}
private void load_ListAnimalActivity(){
setContentView(R.layout.activity_list_animal);
animalList = new ArrayList<HashMap<String,?>>();
new Read_Object().execute();
final ListView listView = (ListView)findViewById(android.R.id.list);
final ListAdapter adapter = new SimpleAdapter(
ListAnimalActivity.this, animalList,
R.layout.row_list, new String[] { "animal_id",
"animal_name","animal_type"},
new int[] { R.id.animal_id, R.id.animal_name,R.id.animal_type });
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
AlertDialog.Builder adb = new AlertDialog.Builder(ListAnimalActivity.this);
adb.setTitle("Attenzione!");
adb.setMessage("Vuoi eliminare l\'elemento \"" + animalList.get(position)+ "\" ?");
final int posizione = position;
adb.setNegativeButton("Annulla",new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {}
});
adb.setPositiveButton("Elimina", new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
new Delete_Object().execute();
}
});
adb.show();
}
});
button_add = (Button)findViewById(R.id.button_add);
button_add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(ListAnimalActivity.this,CRUDAnimalActivity.class));
}
});
}
class Read_Object extends AsyncTask<String, String, String> {
private ProgressDialog progressMessage = new ProgressDialog(ListAnimalActivity.this);
#Override
protected void onPreExecute() {
super.onPreExecute();
progressMessage.setMessage("Loading ...");
progressMessage.setIndeterminate(false);
progressMessage.setCancelable(false);
progressMessage.show();
}
protected String doInBackground(String... args) {
List params = new ArrayList();
JSONObject json = jParser.makeHttpRequest(url_read, "POST", params);
try{
Log.d("Animals: ", json.toString());
} catch (NullPointerException e){
e.printStackTrace();
}
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
animals = json.getJSONArray("animals");
for (int i = 0; i < animals.length(); i++) {
JSONObject c = animals.getJSONObject(i);
String id = c.getString("animal_id");
String name = c.getString("animal_name");
String type = c.getString("animal_type");
HashMap map = new HashMap();
map.put("animal_id", id);
map.put("animal_name", name);
map.put("animal_type", type);
animalList.add(map);
}
}
} catch (JSONException e) {
e.printStackTrace();
} catch (NullPointerException e){
e.printStackTrace();
}
return null;
}
protected void onPostExecute(String file_url) {
progressMessage.dismiss();
runOnUiThread(new Runnable() {
public void run() {
ListAdapter adapter = new SimpleAdapter(
ListAnimalActivity.this, animalList,
R.layout.row_list, new String[] { "animal_id",
"animal_name","animal_type"},
new int[] { R.id.animal_id, R.id.animal_name,R.id.animal_type });
setListAdapter(adapter);
}
});
}
}
class Delete_Object extends AsyncTask<String, String, String>{
private ProgressDialog progressMessage = new ProgressDialog(ListAnimalActivity.this);
#Override
protected void onPreExecute() {
super.onPreExecute();
progressMessage.setMessage("Deleting ...");
progressMessage.setIndeterminate(false);
progressMessage.setCancelable(false);
progressMessage.show();
}
protected String doInBackground(String... args) {
/*
Code to delete
*/
return null;
}
protected void onPostExecute(String file_url){
}
}
}
When i click on a list Item, the listener show me the object in this format:
{animal_type=firstType, animal_name=firstName, animal_id=1}
So my question is:
How can I collect only animal_id from the array animalList > ?
Your animalList is an array list with HashMaps as its elements, so when you call animalList.get(position), it will return a HashMap. To retrieve an animal_id just use :
(animalList.get(position)).get(animal_id).toString();
you have to create a Modelclass/Pojo Class (Private variable and getters and setters) for Animals,
ArrayList<AnimalModel> animalsArrayList = new ArrayList<AnimalModel>();
add the Animals object/data to animalsArrayList and listview.setAdapter(animalsArrayList);
then
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
Object object = speakerListView.getItemAtPosition(position);
AnimalModel animals_data = (AnimalModel) object;
id = animals_data.getAnimal_id()
// call delete Async with thid Id
}
});
You code looks pretty messy. If I were you I would create a Custom Adapter that extends the base adapter and takes as a parameter either a hashmap or a list that you pass in the constructor. There you can have different listeners. Also if you want to notify the activity you can have the "luxury" to pass an interface as a parameter and notify the activity when something changes.
Regarding the json part, I would create two new classes, one which is a Thread Manager that receives tasks and handles them further and another class where u make the http calls and the json parsing.
I have done a similar application that receives data from a nebula interface and displays them to the user.

Android list view current position doesn't work properly

I'm Getting data from my database and pushing them into ListView to view all the data.
Inside my ListView I also have a TextView with the text "Like" in it. Now when I click the like text it will change to "Liked" and I'm updating my like status in my database.
Now my problem is that when i click the "Like" text, the text changes to "Liked" and also it is updated in DB. But also when I scroll through the List View, I can notice other lists' Like text is also changed to Liked. I'm not sure what's going wrong.
I've been trying to get around this problem for quite some days but had no success.
This is my adapter code. At the bottom you can see my onClickListener for the textview
package com.mytestapp.myapp;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class ForumAdapter extends ArrayAdapter<DiscussionList> {
private static List<DiscussionList> items = null;
public ForumAdapter(Context context, List<DiscussionList> items) {
super(context, R.layout.custom_list, items);
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
public static DiscussionList getModelPosition(int position) {
return items.get(position);
}
public void refill(List<DiscussionList> items) {
items.clear();
items.addAll(items);
notifyDataSetChanged();
}
public static class ViewHolder {
WebView mywebviewholder;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
View v = convertView;
if (v == null) {
LayoutInflater li = LayoutInflater.from(getContext());
v = li.inflate(R.layout.custom_list, null);
} else {
holder = (ViewHolder) convertView.getTag();
}
DiscussionList app = items.get(position);
if (app != null) {
TextView titleText = (TextView) v.findViewById(R.id.dscTitle);
TextView categoryText = (TextView) v.findViewById(R.id.dscCategory);
TextView descriptionText = (TextView) v
.findViewById(R.id.dscDescription);
TextView timeText = (TextView) v.findViewById(R.id.dscTime);
TextView idText = (TextView) v.findViewById(R.id.dscDiscId);
final TextView likeText = (TextView) v.findViewById(R.id.likeText1);
String like_Status = app.getLikeStatus();
titleText.setText(app.getTitle());
categoryText.setText(app.getCategory());
descriptionText.setText(app.getDescription());
timeText.setText(app.getTime());
idText.setText(app.getDiscId());
if (like_Status == "null") {
likeText.setText("Like");
} else {
likeText.setText("Liked");
}
final String dId = app.getDiscId();
// onClick for image button inside list view
likeText.setTag(new Integer(position));
likeText.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final Integer myposition = (Integer) view.getTag();
// Toast.makeText(getContext(), "" + dId,
// Toast.LENGTH_SHORT)
// .show();
likeText.setText("Liked");
MainActivity val = new MainActivity();
val.updateLikeTable(dId);
}
});
}
return v;
}
}
And also this is my MainActivity.java file where i update the likes in database
package com.mytestapp.myapp;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.StrictMode;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity extends ListActivity implements FetchDataListener {
public static String strTitle = "0", strCategory = "0",
strDescription = "0", strTime = "0", strDid = "0";
Collegemate_DB db = new Collegemate_DB(this);
int likeStatus = 1;
private ProgressDialog dialog;
public static String usId=null;
ImageButton imgButton;
List<DiscussionList> items = new ArrayList<DiscussionList>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_forum_topics);
usId = db.getCurrentuserId();
initView();
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
.permitAll().build();
StrictMode.setThreadPolicy(policy);
}
/*
* On click listener to get values from DiscussionList class and send it
* to another activity when clicking on the list item
*/
ListView forumList = getListView();
forumList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// Get position of the clicked list item from adapter
String dTitle, dCategory, dDescription, dTime, dDid;
DiscussionList accessVar = ForumAdapter
.getModelPosition(position);
dTitle = accessVar.getTitle();
dCategory = accessVar.getCategory();
dDescription = accessVar.getDescription();
dTime = accessVar.getTime();
dDid = accessVar.getDiscId();
/*
* Storing the forum values in string and passing it to another
* activity
*/
String values[] = { dTitle, dCategory, dDescription, dTime,
dDid };
Intent i = new Intent(MainActivity.this, ForumFullView.class);
i.putExtra("sendData", values);
startActivity(i);
}
});
}
private void initView() {
// show progress dialog
Log.i("j","Inside Init");
dialog = ProgressDialog.show(this, "", "Loading...");
String url = "http://example.com/mypath/listData.php?currentUser_id="
+ usId;
Log.i("Fetch Url : ", url);
FetchDataTask task = new FetchDataTask(this);
task.execute(url);
}
#Override
public void onFetchComplete(List<DiscussionList> data) {
// dismiss the progress dialog
if (dialog != null)
dialog.dismiss();
// create new adapter
ListView forumList = getListView();
// set the adapter to list
ForumAdapter adapter = new ForumAdapter(this, data);
if (forumList.getAdapter() == null) {
//final ForumAdapter adapter = new ForumAdapter(this, data);
forumList.setAdapter(adapter);
} else {
((ForumAdapter) forumList.getAdapter()).refill(items);
}
// setListAdapter(adapter);
}
#Override
public void onFetchFailure(String msg) {
// dismiss the progress dialog
if (dialog != null)
dialog.dismiss();
// show failure message
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
public void updateLikeTable(String dId,List<DiscussionList> items) {
try {
String likeUrl = "http://example.com/mypath/createlike.php?discId="
+ dId + "&userId=" + usId + "&like_status=" + likeStatus;
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet();
request.setURI(new URI(likeUrl));
client.execute(request);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Any help is appreciated.
Thanks in advance
The problem you're having is that the ListView widget recycles its views if it can. Once a view is off the screen from scrolling, it goes onto a garbage heap so that when a new view scrolls into place it can be reused, rather than requiring a fresh one to be inflated from scratch. That recycled view is the convertView parameter in the getView() method. When your ListView is first populating, convertView will always be null, since the garbage pile has nothing in it, so you're forced to inflate new views, but subsequent calls will likely have that parameter as non-null.
The practical result of this is that when a clicked view that's been set to "Liked" gets recycled, the TextView is still there and still populated with "Liked" rather than the presumed default of "Like". So if you click a view, then scroll down so it goes off the screen, it'll come back around and cause the bug you're seeing.
What you'll probably want to do to fix this is to set the text of likeText within getView() every time, based on what it is in your database. If the post has been liked, set it to "Liked", and if it hasn't, set it to "Like". It should just be one more line, assuming you have easy access to whether or not the post is liked from your DiscussionList object.
P.S. As a side note, hard-coded strings are typically frowned upon in Android, so you may want to move your "Liked" string into the resource files. It's not really necessary unless you're planning to do translations, but it's still good practice.

Out-of-sync ArrayAdapter with Autocomplete

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

Categories

Resources