I am new to android and having some trouble with this method and wondering if someone can assist with this.
Question: I have an array adapter with EditText box that the user can modify in the ListView. But, I only want user modifications and not the program's in my fragment. How do I do this?(read on for full context) The issue here is that when the user modifies this EditText in the listView, the adapter will make a call to the fragment and the fragment will manipulate the numbers. Then it will modify the other EditText in the list view.
The problem for me is, when the user modifies or clicks on the EditText in the list view, it's value using afterTextChanged() will be passed to the fragment, but since i do some modification and modify the same EditText in different positions in the list view programmatically, the afterTextChanged events gets called for other EditText views in the list view as well. I just want user changed.
To tackle this, I tried using this onTouchListener(). Just want to know if this is correct.
Class myArrayAdapter extends ArrayAdapter<Obj>{
//private defines and static viewHolder class
public myArrayAdapter (Context c, ArrayList<Obj> obj, MyFragment myFrag
{
//this....
}
#Override
public View get View (int position, View convertView, ViewGroup parent){
obj posObj = getItem(position);
if (convertView == NULL){
//inflate (ObjlistView)
viewHolder.userInputValue = (EditText) covertView.findViewById(R.id.view_userInput);
//user onTouch
final int pos= position;
viewHolder.userInputValue.setOnTouchListener(new View.OnTouchListener(){
#Override
public boolean onTouch(View view, MotionEvent event){
EditText UserInput= (EditText)view;
//this is a custom Text watcher for passing editText Value to Fragment. SO I want this to be only called when user modifies it. Not when the Fragment modifies it and calling notifyDataSetChanged
UserInput.addTextChangedListener(new CustomTextWatcher(pos, myFrag));
return false;
}
});
convertView.setTag(viewHolder)
}else {//...}
viewHolder.userInputValue.setText(posObj.getValue());
}
}
XML for the ListVie
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="number" // another annoying issue when i user clicks the editText it will show the number pad then will go to the keyboard and every time there is a delete or a new entry this keyboard change will happen. But that is not for this question.
android:id="#+id/view_userInput"
In the fragment this is how I am modifying the edittext again
Obj myTempUserobj ;
for (int i = 0; i< userObj.size(); i++){
myTempUserobj = userObj.get(i);
//modify the field in the object
userObj.set(i,myTempUserobj)
myAdapter.notifyDataSetChanged();
}
Thanks for your Help!
Related
Maybe there is a simple solution to my problem, but i´m not able to find it.
I have a ListView with a list of users. Each row has an EditText to enter the username. I want to set hint text for each user like: "user1, user2, user3, etc", used as default name. The user can click on the EditText and change this name, so the hint text dissapears and the user can enter his name, or leave this default name if he wants.
When the ListView is too long, I have the problem of the view recycling, that duplicates the names. I solved it by using a setOnFocusChangeListener for the EditText, and storing the name for each row, and it´s working fine, but I´d like that when I have a long list, keep the hint text or text introduced by the user for each EditText when scrolling the list.
I don´t know how to modify my adapter to set the name/hint for each EditText.
Any idea?
Thanks a lot.
Create one list in your activity
List<String> yourlist = new ArrayList();
yourlist.add("user1");
yourlist.add("user2");
yourlist.add("user3");....
pass this list to adapter then in adapter,
holder.youredittext.setHint(yourlist.get(position));
if your passing number of items to adapter then create one model class then pass to adapter.
I have an adapter like this:
public View getView(final int position, View convertView, ViewGroup parent)
{
View item = convertView;
if(item == null)
{
LayoutInflater inflater = context.getLayoutInflater();
item = inflater.inflate(R.layout.setup_jugador, null);
holder = new ViewHolder();
holder.nombre = (EditText) item.findViewById(R.id.nombre);
// Establecemos el tag
item.setTag(holder);
}
else
{
holder = (ViewHolder)item.getTag();
}
holder.nombre.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if(!hasFocus){
EditText et = (EditText) v.findViewById(R.id.nombre);
// Here I store the name
jugadores.get(position).setNombre(et.getText().toString());
}
}
});
// Update EditText
holder.nombre.setText(jugadores.get(position).getNombre());
// Another option is
holder.nombre.setHint(jugadores.get(position).getNombre());
return(item);
}
jugadores is a list that stores the names. Initially it has user1, user2, etc.
I could setHint when Item==null, but I have to update the text at the end of the adapter, and when scrolling, the view recycling changes the items that are invisible.
I only see 8 items, and when scrolling, if I change first item, item number 9 also change. If I use setText, it becomes black, and if I use setHint, first item becomes grey.
I can´t put hint value in layout because I´d like to add the row number to the name. I tried using a boolean value in the class used as model in the adapter to show that the name has been modified, check this value using position index in the list, and use setText or setHint according to that, but doesn´t work.
I have an ExpandableListView. Each child has a CheckBox and a TextView. When a user taps a child row, the CheckBox is supposed to change its state (checked vs unchecked). This works correctly if the user taps directly on the check box. However, if the user taps on the text view (same row, immediately to the right of the check box), I get a null pointer error as soon as I try to refer to the check box. Can anyone see what is wrong?
EDIT: After reading the suggestion below, I did some investigation and realized that I can implement my clickListener inside my adapter under getChildView(). This solves my issue with the null pointer as I can easily get a reference to the child view.
However, it creates another issue that I see no elegant solution to. Each time a child is clicked, I need to make changes to the listview itself. The data for this list resides in an ArrayList whose scope is within the Activity (not in the Adapter). If my clickListener is in the Adapter, how can I call back to the Activity to make changes to the ArrayList?
This strikes me as a catch-22. If I want to be able to manipulate my data, I can't get a reference to the child view. But if I want a reference to my child view, my data is out of scope, so I can't manipulate it. How do people resolve this? I must be missing something.
I'll throw in the relevant adapter code where you can see the beginning of my attempt to add a child onClickListener.
Thanks again!
public class Settings extends Activity {
//this is the list I need to access from the adapter if my click listener is there
private ArrayList<Categories> categoriesList = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_smart_settings);
db = new EventsDB(this);
expListGroups = new ArrayList<ExpandListGroup>();
setGroups();
/* Set up the data adapter */
expAdapter = new ExpandListAdapter(Settings.this, expListGroups);
populateExpandableGroups();
}
public void setGroups() {
/* Create lists of the individual line items */
categoriesList = db.getCategoriesForClient(client);
locationsList = db.getLocationsForClient(client);
/* Add an item to the locations list to allow the user to add a new location */
locationsList.add(new ClientSmartFinderLocations(client, "Add Location", null, false));
ExpandListGroup categoryGroup = new ExpandListCategory("Choose Categories", categoriesList);
ExpandListGroup locationGroup = new ExpandListLocation("Choose Locations", locationsList);
expListGroups.add(categoryGroup);
expListGroups.add(locationGroup);
}
private void populateExpandableGroups() {
expandableList = (ExpandableListView) findViewById(R.id.expandable_list);
expandableList.setAdapter(expAdapter);
//I've removed this section and moved it to the adapter, per my edit above
// expandableList.setOnChildClickListener(new OnChildClickListener() {
//
// #Override
// public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
//
// /* Update the finder setting for this client */
// String category = categoriesList.get(childPosition).getCategory();
// Boolean isSelected = !categoriesList.get(childPosition).getIsSelected();
// db.setClientCategory(client, category, isSelected);
//
// /* Update the check box to provide feedback to the user */
// View view = parent.getChildAt(childPosition - parent.getFirstVisiblePosition() + 1);
// CheckBox checkBox = (CheckBox) view.findViewById(R.id.check_box);
//
// //error occurs here
// checkBox.setChecked(!checkBox.isChecked());
// return true;
// }
// });
expAdapter.notifyDataSetChanged();
}
public class ExpandListAdapter extends BaseExpandableListAdapter {
//other methods
#Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View view, final ViewGroup parent) {
view = getCategoryChildView(groupPosition, childPosition, view);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TextView tv = (TextView) v.findViewById(R.id.expand_list_item);
CheckBox checkBox = (CheckBox) v.findViewById(R.id.check_box);
String category = tv.getText().toString();
Boolean isSelected = !checkBox.isSelected();
db = new EventsDB(context);
db.setClientCategory(client, category, isSelected);
checkBox.setChecked(!checkBox.isChecked());
//here I need to do some things that require me to manipulate the categoriesList from the Activity class - but it is out of scope
}
});
return view;
}
}
<CheckBox
android:id="#+id/check_box"
android:layout_marginLeft="30dp"
android:focusable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/expand_list_item"
android:paddingLeft="10dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="#dimen/smart_finder_settings_font_size"
android:textColor="#FFFFFF" />
I think that I have a solution. If I create an instance of the Settings activity class within the Adapter, I can add the usual getter/setter methods to the Settings class. I can then call these methods from the adapter to get at that list, modify it, and then push it back to the Settings class, and all should be well.
It's a basic OOP approach, but the mental hangup for me was the idea of instantiating an activity. I don't know if this is common or not, but it seems weird to me. Anyone have a more elegant solution?
Within the Settings class:
public ArrayList<Categories> getCategoriesList() {
return categoriesList;
}
public void setCategoriesList(ArrayList<Categories> list) {
categoriesList = list;
}
Within the adapter:
Settings settings = new Settings();
ArrayList<Categories> tempCategoriesList = new ArrayList<Categories>();
tempCategoriesList = settings.getCategoriesList();
//make changes to the list
settings.setCategoriesList(tempCategoriesList);
You should set the checkbox checked state inside your ExpandListAdapter instead of accessing the view inside the ExpandableListView directly.
Im not a pro, so take that in mind. but what about storing the data you need the clicklistener to manipulate in a gobal application class? Maybe not the most elegant, but might work.
user55410 is on the right track. In your Settings() Activity, make categoryList static, then when you change it with the getter/setter methods from the new instance you create in your it will modify all instances of Settings.categoryList.
My app contains 25 edittexts. I am getting this 25 edittexts with the help of adapter class by giving count=25 and fitting in gridView by gridView.setAdapter(new TextAdapter(this)); in the activity class. So, the edittexts are dynamically generated. But the thing is I am unable to set the initial values in the edittexts. This is because the edittext objects are unavailable to set the values.
Suppose if I don't set any initial values in the edittexts and continue with my app. The same problem repeats while setting the values back in the edittexts which are entered in previous mode after changing the orientation. Because change in orientation creates new activity. Even I tried android:configChanges="orientation|keyboardHidden", but no use while I am setting the values back in the **onConfigurationChanged()**. Because I am setting the setContentView(); in the onConfigurationChanged() as I need the respective view, but still the edittext objects are unavailable to set their values.
Is there any solution to set back the values? If not, I am thinking(Might be completely wrong way, but as a newbie please go easy) to move the onCreate() method content to Application class. So the initial part goes to Application class including the creation of edittexts. and getting that edittext objects in the onCreate() method to set the values. Is it possible? Please suggest. Code snippet would be appreciated.
You will need to modify TextAdapter. Store the initial values in a String array, with the position of the String array element aligned to the position of the EditText in your GridView.
Pseudo-code (untested):
public class TextAdapter extends BaseAdapter {
String [] initial_value = {"Initial Value 1", "Initial Value 2", "Initial Value 3", ..., };
public View getView(int pos, View view, ViewGroup viewGroup) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.edit_text_container, null);
}
(EditText) edtTemp = (EditText) view.findViewById(R.id.edit_text_id);
edtTemp.setText(initial_value[pos]);
}
}
I have EditText inside ListView for inline editing. The user can modify its value & the values are then stored in the SQLite database.
I am using Custom DataAdapter for populating ListView.
There are 15 rows in the listview. at a time 4 rows are visible, to see other rows scrollbar is used (Up / Down)
Issue:
ListView is loaded for the first time. User writes into the EditText 1,2,3,4 in each EditText.
The Scrollbar is moved down & one by one the user enters data into all the rows.
When the user moves the Scroll bar UP to see the first 4 rows... the 4th row shows wrong data instead of 4 it shows 14, which is the last value entered by the user (OR my have some other random value forexample 10/13/11 etc)
In my Custom DataAdapter class i am storing the user input into the datasource when the EditText loses Focus as shown below:
public View getView(int position, View convertView, ViewGroup parent) {
//txtComments is an EditText
holder.txtComments.setId(position);
holder.txtComments.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus){
final int position = v.getId();
final EditText tempComments = (EditText) v;
// items: the DataSource for the listView
items.get(position).Comments = tempComments.getText().toString();
}
}
});
}
EDIT:
I have also tried the TEXTWATCHER but it doest not work for me.
After extensive testing; we found that this issue occures randomly for example sometimes ExitText11 will show wrong value another time ExitText4 will show wrong value OR both of them will show wrong values & this behaviour changes while moving scrollbar up down.
I have created a custom Array Adapter to bind a custom row that contains some static text and an editable EditText. I am trying to register to be notified when the user changes the text within the edit text and when notified to determine which ArrayList row the modified EditText corresponds to.
In the past with other types of views such as a Spinner I could simply put a reference to the parent view and the row number into the tag for the Spinner view. And then when I was notified that the value changed I read the tag to determine how to correlate it back to the master ArrayList.
The problem with registering to be notifed with an EditText change is that you do not get back a view but instead get a TextWatcher and I have no way to correlate back to the parent view or ArrayList row.
What is the technique that you need to use in this circumstance?
You can use onEditorAction on your EditText in your ArrayAdapter:
mEditText.setOnEditorActionListener(new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
// Parse parent tree to find position of view
int position = 0;
View v = null;
while (view != v && position < mListView.getChildCount())
v = mListView.getChildAt(position++);
// do something
something(position);
// do not consume the action
return false;
}
});
Note that using this method, you are going to trigger an event only when user press "ok", "enter", "done", etc. on the keyboard.