问题
I have a listview
with edittext
widgets inside each row. I use a viewholder
inside my custom adapter
to keep track of all the views.
But my problem is that when i input a value inside my edittext
, pause until my screen times out, when you unlock the phone while still in the same activity
,the default edittext
value is refilled, overwriting my edits.
I followed this suggestion here (when listview scroll that time edittext set default value) considering that maybe i am not using the viewholder
right but still facing the same issue. It's like getView()
keeps getting called, completely redrawing my views.
Any ideas/suggestions on a workaround?
package com.shop.shopOfficer;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
import com.shopOfficer.R;
import java.util.ArrayList;
import java.util.List;
/**
* Created by steve on 2/15/16.
*/
public class ProductListAdapter extends BaseAdapter implements Filterable {
List<ProductModel> mStringFilterList;
ValueFilter valueFilter;
private Activity activity;
private LayoutInflater inflater;
private List<ProductModel> modelItems;
private AddRemoveProductInterface myActivityInterface;
public ProductListAdapter(Activity activity, List<ProductModel> modelItems, AddRemoveProductInterface myActivityInterface) {
this.activity = activity;
this.modelItems = modelItems;
mStringFilterList = modelItems;
this.myActivityInterface = myActivityInterface;
}
@Override
public int getCount() {
return modelItems.size();
}
@Override
public Object getItem(int location) {
return modelItems.get(location);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolderItem viewHolder;
if (convertView == null) {
// inflate the layout
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.product_item_row, null);
// well set up the ViewHolder
viewHolder = new ViewHolderItem();
viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv2);
viewHolder.price = (TextView) convertView.findViewById(R.id.price);
viewHolder.p = (TextView) convertView.findViewById(R.id.p);
viewHolder.minus = (Button) convertView.findViewById(R.id.minus);
viewHolder.add = (Button) convertView.findViewById(R.id.add);
viewHolder.quantity = (EditText) convertView.findViewById(R.id.num);
viewHolder.quantity.addTextChangedListener(new MyTextWatcher(convertView, position));
viewHolder.add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//get the value of edittext
//add one item
int added_item = Integer.parseInt(viewHolder.quantity.getText().toString()) + 1;
viewHolder.quantity.setText("" + added_item);
}
});
viewHolder.minus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int removed_item = Integer.parseInt(viewHolder.quantity.getText().toString()) - 1;
if (removed_item >= 0) {
viewHolder.quantity.setText("" + removed_item);
} else {
}
}
});
// store the holder with the view.
convertView.setTag(viewHolder);
} else {
// we've just avoided calling findViewById() on resource everytime
// just use the viewHolder
viewHolder = (ViewHolderItem) convertView.getTag();
}
// object item based on the position
final ProductModel m = modelItems.get(position);
viewHolder.tvTitle.setText(m.getname());
viewHolder.price.setText("(" + m.getPrice() + ")");
viewHolder.p.setText(m.getPrice());
viewHolder.quantity.setTag(m);
viewHolder.quantity.setText(String.valueOf(m.getTQuantity()));
convertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(inflater.getContext(), EditProduct.class);
intent.putExtra("name", m.getname());
intent.putExtra("price", m.getPrice());
intent.putExtra("description", m.getproductDesc());
intent.putExtra("image_url", m.getImage_url());
inflater.getContext().startActivity(intent);
}
});
return convertView;
}
@Override
public Filter getFilter() {
if (valueFilter == null) {
valueFilter = new ValueFilter();
}
return valueFilter;
}
private class ValueFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (constraint != null && constraint.length() > 0) {
ArrayList<ProductModel> filterList = new ArrayList<>();
for (int i = 0; i < mStringFilterList.size(); i++)
if ((mStringFilterList.get(i).getname().toUpperCase())
.contains(constraint.toString().toUpperCase()) ||
(mStringFilterList.get(i).getproductDesc().toUpperCase())
.contains(constraint.toString().toUpperCase())) {
ProductModel m = new ProductModel(mStringFilterList.get(i)
.getname(), mStringFilterList.get(i)
.getproductId(), mStringFilterList.get(i)
.getImage_url(), mStringFilterList.get(i)
.getPrice(), mStringFilterList.get(i)
.getproductDesc());
filterList.add(m);
}
results.count = filterList.size();
results.values = filterList;
} else {
results.count = mStringFilterList.size();
results.values = mStringFilterList;
//show no results were picked
//(myActivityInterface).onSearchEmpty("No results found");
}
return results;
}
@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
//if(results)
modelItems = (List<ProductModel>) results.values;
if (modelItems.size() > 0) {
notifyDataSetChanged();
} else {
(myActivityInterface).onSearchEmpty("No results found");
}
}
}
static class ViewHolderItem {
TextView tvTitle, price, p;
Button add, minus;
EditText quantity;
}
private class MyTextWatcher implements TextWatcher {
View view;
int position;
public MyTextWatcher(View convertView, int position) {
this.view = convertView;
this.position = position;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
EditText qtyView = (EditText) view.findViewById(R.id.num);
// ProductModel m = modelItems.get(position);
String qtyString = s.toString().trim();
int quantity = qtyString.equals("") ? 0:Integer.valueOf(qtyString);
ProductModel m = (ProductModel)qtyView.getTag();
if(m.getTQuantity() != quantity) {
m.setTQuantity(quantity);
String price = ((TextView) view.findViewById(R.id.p))
.getText().toString();
String name = ((TextView) view.findViewById(R.id.tv2))
.getText().toString();
int database_position = 1 + position;
Log.d("my position", "" + position);
Log.d("my value", s.toString() + price);
(myActivityInterface).onAdded(s.toString().trim(), price, database_position, name);
}
}
}
}
Activity code:
public class ProductList extends AppCompatActivity implements AddRemoveProductInterface {
ListView listView, checkout_listview;
EditText inputSearch;
ProductListAdapter adapter;
ProductCheckoutAdapter checkout_adapter;
ProductHandler productDB;
String tag_json_obj = "json_obj_req";
TextView total;
String unformatted_number;
String c_phone, c_zip, c_name, total_amount, checkout_id;
Boolean c_extras;
fr.castorflex.android.smoothprogressbar.SmoothProgressBar progbar;
Button button;
byte[] b;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_product_list);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
total = (TextView) findViewById(R.id.total);
total.setText("0.00");
Intent i = getIntent();
Bundle extras = i.getExtras();
if (extras == null) {
//is null
c_extras = false;
} else {
//has extras
c_extras = true;
b = extras.getByteArray("picture");
c_phone = i.getStringExtra("phone");
c_zip = i.getStringExtra("zip");
c_name = i.getStringExtra("name");
total_amount = i.getStringExtra("total_amount");
checkout_id = i.getStringExtra("checkout_id");
if (total_amount == null) {
total.setText("0.00");
} else {
total.setText(total_amount);
}
// Toast.makeText(getApplicationContext(), total_amount, Toast.LENGTH_LONG).show();
}
productDB = new ProductHandler(this);
/* sbv = (SlideBottomPanel) findViewById(R.id.sbv);*/
progbar = (fr.castorflex.android.smoothprogressbar.SmoothProgressBar) findViewById(R.id.prog1);
listView = (ListView) findViewById(R.id.list);
// listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
checkout_listview = (ListView) findViewById(R.id.checkout_list);
// insert data into the list before setting the adapter
// otherwise it will generate NullPointerException - Obviously
productListRequest();
showProductFromTable();
inputSearch = (EditText) findViewById(R.id.inputSearch);
inputSearch.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
// When user changed the Text
/* ProductList.this.myList.getFilter().filter(cs);*/
if (cs.length() > 0) {
adapter.getFilter().filter(cs);
} else {
TextView search = (TextView) findViewById(R.id.no_results);
search.setVisibility(View.INVISIBLE);
adapter.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
}
});
final LinearLayout animated_layout = (LinearLayout) findViewById(R.id.list_animated);
//animating product sold list
button = (Button) findViewById(R.id.submit);
if (total.getText().toString().equals("0.00")) {
button.setEnabled(false);
} else {
button.setEnabled(true);
}
button.setOnClickListener(new View.OnClickListener() {
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public void onClick(View v) {
Log.d("JSON PROD:", productDB.composeProductSolddJSONfromSQLite());
Intent intent = new Intent(getApplicationContext(), CustomerIdentify.class);
intent.putExtra("total_sld", unformatted_number);
//send extras if exist to identify activity
if (c_extras) {
intent.putExtra("picture", b);
intent.putExtra("c_phone", c_phone);
intent.putExtra("c_extras", c_extras);
intent.putExtra("c_zip", c_zip);
intent.putExtra("c_name", c_name);
}
startActivity(intent);
}
});
}
private void showProductFromTable() {
//progbar.setVisibility(View.INVISIBLE);
ArrayList<ProductModel> modelArrayList = productDB.loadProduct();
//adding it to the list view.
adapter = new ProductListAdapter(this, modelArrayList, this);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
public void productListRequest() {
progbar.setVisibility(View.VISIBLE);
String url = "http://shopofficer.com/business/products/api";
JsonArrayRequest jsonObjReq = new JsonArrayRequest(url,
new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray response) {
Log.d("product list response:", response.toString());
//listView.setVisibility(View.VISIBLE);
progbar.setVisibility(View.INVISIBLE);
// Parsing json
for (int i = 0; i < response.length(); i++) {
try {
JSONObject obj = response.getJSONObject(i);
String name = obj.getString("name");
String price = obj.getString("price");
String description = obj.getString("description");
String image = obj.getString("image");
String product_id = obj.getString("id");
/*Log.d("my data is", id + title + description);*/
productDB.addProduct(product_id, name, price, description, image, "0");
showProductFromTable();
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d("productlist error",
"Error: " + error.getMessage());
/* Crouton.makeText(AttendantList.this, "Something went wrong, please retry",
Style.ALERT, R.id.anchor).show();*/
progbar.setVisibility(View.INVISIBLE);
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("session_id", getapikey());
return headers;
}
};
ShopOfficer.getInstance().getRequestQueue().getCache()
.invalidate(url, true);
// Adding request to request queue
ShopOfficer.getInstance().addToRequestQueue(jsonObjReq, tag_json_obj);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_select_product, menu);
return true;
}
@Override
public void onResume() {
super.onResume();
showProductFromTable();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_add_product) {
Intent intent = new Intent(getApplicationContext(), AddNewProduct.class);
startActivity(intent);
return true;
}
if (id == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onAdded(String s, String price, int database_position, String name) {
if (s.equals("")) {
} else {
//set total
Double currentPrice = Double.valueOf(price);
Double quantity = Double.valueOf(s);
Double calculated = quantity * currentPrice;
//Double priceDiff = Double.valueOf(df.format(extPrice - currPrice));
productDB.update(database_position, "" + calculated);
DecimalFormat df = new DecimalFormat("0.00##");
//add commas thousands
unformatted_number = String.valueOf(df.format(productDB.getTotalOfAmount()));
double amount = Double.parseDouble(unformatted_number);
DecimalFormat formatter = new DecimalFormat("#,###.00");
total.setText("" + formatter.format(amount));
if (total.getText().toString().equals(".00")) {
total.setText("0.00");
}
//add product sold
if (quantity < 1) {
productDB.deleteSingleProductSold(Integer.valueOf(s));
} else {
productDB.addSale(database_position, name, String.valueOf(calculated), s);
}
if (total.getText().toString().equals("0.00")) {
button.setEnabled(false);
} else {
button.setEnabled(true);
}
}
}
@Override
public void onRemoved(String s) {
total.setText("");
}
@Override
public void onSearchEmpty(String s) {
// Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show();
TextView search = (TextView) findViewById(R.id.no_results);
search.setVisibility(View.VISIBLE);
search.setText(s);
}
}
回答1:
The views and the viewholders are only temporarily associated with a row. It means that any edited data needs to be stored elsewhere. Otherwise the information is lost every time the views are rebuild or reused for a different row, like when scrolling or rebuilding the view.
One possible solution is to add it to the model :
Add the data (quantity) to the
ProductModel
. (the_quantity
)Add a position attribute to the viewholder (
the_position
). This is needed because the listeners are anonymous classes. They see (a copy of) the value of theposition
parameter with the value it had when they were instanciated.
This is why the compiler sometimes complains that theposition
parameter is not final, and it usually means that something is wrong : You should only reference it outside callbacks.UI -> model : Store the edited value in model (in both listeners)
public void onClick(View v) { //get the value of edittext //add one item int added_item = Integer.parseInt(viewHolder.quantity.getText().toString()) + 1; // Store the value in the model // We use the position from the viewholder, // **not** the method's parameter (which contain // the value when the listener was created) modelItems.get(viewHolder.the_position).the_quantity = added_item; viewHolder.quantity.setText("" + added_item); }
Model -> UI : Update the UI each time, not only when the views a created
// object item based on the position final ProductModel m = modelItems.get(position); viewHolder.tvTitle.setText(m.getname()); // update the viewholder's position viewHolder.the_position = position; viewHolder.quantity.setText("" + m.the_quantity) return convertView;
来源:https://stackoverflow.com/questions/35939762/edittext-values-in-listview-changes-to-default-on-screen-timeout