I am having a spinner that should be filled with strings.But can not use binding.mySpinner to set adapter.
The Goal is to have two spinners first is with two country USA and CANADA so when user click on Usa second spinner should list all states.Same thing for Canada.I managed to find out what country is clicked and i stored that value in String.I wanted to use simple If/else statement but do not know how to fill second spinner.
my xml:`
<Spinner
android:id="#+id/states_spinner"
style="#style/Widget.AppCompat.DropDownItem.Spinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="50dp"
app:layout_constraintBottom_toTopOf="#+id/submit_BTN"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/states_TV" />
`
and my ViewModel:
public AdapterView.OnItemSelectedListener countryListener = new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Object item = parent.getItemAtPosition(position).toString();
switch (position){
case 0:
billingInfo.setCountry("Usa");
selectedCountry = billingInfo.getCountry();
List<String> listUsa = new ArrayList<String>(Arrays.asList(SpinnerEntriesUtils.usaArray));
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
SuccessPlanApplication.getContext(), android.R.layout.simple_spinner_item,listUsa );
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
break;
case 1:
billingInfo.setCountry("Canada");
selectedCountry = billingInfo.getCountry();
break;
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
};
Related
I have implemented a list View with CheckedTextView . When I am selecting a particular row and clicking on checkbox then checkbox becomes invisible. Also sometimes any other row get selected.I want to select multiple items.
please help...
List_row Layout is..
<CheckedTextView
android:id="#+id/service_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="20sp"
android:layout_marginBottom="10dp"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
android:textColor="#000000"
android:checked= "false"
>
CustomListView is:
import java.util.ArrayList;
public class CustomListView extends ArrayAdapter {
//to reference the Activity
private final Activity context;
String value;
//to store the list items
private final String[] nameArray;
CheckedTextView nameTextField;
public CustomListView(Activity context, ArrayList nameArra) {
super(context, R.layout.row_list_view, nameArrayParam);
this.context = context;
this.nameArray = nameArrayParam;
}
public View getView(int position, View view, ViewGroup parent)
{
LayoutInflater inflater = context.getLayoutInflater();
View rowView = inflater.inflate(R.layout.row_list_view, null, true);
//this code gets references to objects in the listview_row.xml file
nameTextField = (CheckedTextView)
rowView.findViewById(R.id.service_name);
//this code sets the values of the objects to values from the arrays
nameTextField.setText(nameArray[position]);
// perform on Click Event Listener on CheckedTextView
nameTextField.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.v("customList",";clicked row is " +
nameTextField.getText().toString());
if (nameTextField.isChecked()) {
// set check mark drawable and set checked property to false
value = "un-Checked";
nameTextField.setCheckMarkDrawable(R.color.colorAccent);
nameTextField.setChecked(false);
}
else {
// set check mark drawable and set checked property to true
value = "Checked";
nameTextField.setChecked(true);
}
Toast.makeText(context, value, Toast.LENGTH_SHORT).show();
}
});
return rowView;
}
In my MainActivity.java, I have used listView setOnItemClickListener
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
selected_Item = (String) arg0.getItemAtPosition(arg2);
Toast.makeText(AskForService.this, "Clicked item is" + selected_Item, Toast.LENGTH_LONG).show();
}
});
After implementing these code I can see -
ListView with checked TextView
ListView Can Scroll showing all the list items
on clicking a particular row..listview click listener is called and showing the Toast but checkbox becomes white(invisible) and sometimes another row get selected...Please help
Create a model class which contains a flag "isChecked" and set the data according to the model inside adapter
This link might be helpful : https://stackoverflow.com/a/40285759/8770539
Use an arraylist to store checked item's position.
call notifyDataSetChanged() for onclick()
Use this,
if(list.contains(position)) {
nameTextField.setChecked(true);
}
else {
nameTextField.setChecked(false);
}
nameTextField.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(list.contains(position))
{
list.remove(position);
}
else
{
list.add(position);
}
notifyDataSetChanged();
}
});
Scenario: Let's say there are 2 spinner in the fragment called spinner a and b:
Spinner a contain: dog cat elephant
Spinner b contain: micky, minny, moe
My question is: Is it possible that when user using apps, and clicked Spinner a, and choose elephant, the micky and minny option in Spinner b option be gone and only leaving moe as an option?
And what is the best way to do it? (not using any database)
xml:
<Spinner
android:id="#+id/spinner_a"
android:entries="#array/value_a"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<Spinner
android:id="#+id/spinner_b"
android:entries="#array/value_b"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
string value:
<resources>
<string-array name="value_a">
<item>dog</item>
<item>cat</item>
<item>Elephant</item>
</string-array>
<string-array name="value_b">
<item>micky</item>
<item>minny</item>
<item>moe</item>
</string-array>
java:
package...
import....
public class testing123 extends Fragment{
final Spinner a123 = (Spinner) getView().findViewById(R.id.spinner_a);
final Spinner b123 = (Spinner) getView().findViewById(R.id.spinner_b);
final String ax = a123.getSelectedItem().toString();
final String bx = b123.getSelectedItem().toString();
if (ax.equals("elephant")) {
//what is the best way to do it?
}else{}
}
Yes you can but not with your way.
For spinner a it's ok, but for b you should manage it dinamically from Java code.
Add to spinner A a listener and modify string-array of spinner b based on item a selected.
Listener:
Spinner spinner_a = (Spinner) findViewById(R.id.spinner_a);
spinner_a.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// retrieve selected item
String spinner_a_choice = parent.getItemAtPosition(position).toString();
// elaborate and set here your spinner b with its own adapter
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
// Something else
}
});
How to add an adapter to spinner (you have to manipulate it):
Spinner spinner_b = (Spinner) findViewById(R.id.spinner_b);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.value_b, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner_b.setAdapter(adapter);
I have search on StackOverflow and other websites but no one can answer to my question.
I have a gridView with items. I have a button to add item to this gridView.Each element on the GridView is a relativeLayout with an Imageview and an EditText.
When i add item to the gridView using the button, I want to get my relativeLayout and request Focus on the editText to set a name on it.
Imagine i have 3 elements in my gridView.
I add element to my ArrayList and call adapter.notifiyDataSetChanged().
The new element is displayed on the grid but when i use getChildCount(), the gridView still has 3 children.
It cause problem because i want to request focus on the last added EditText.
How can i update my gridView object ?
Fragment :
//Get gridView
final GridView gridCat = (GridView) v.findViewById(R.id.gridCategory);
adapter = new GridCategoryAdapter(getActivity(), subcatList);
gridCat.setAdapter(adapter);
gridCat.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
SubCategory subcat = subcatList.get(position);
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_middle, SubCategoryFragment.newInstance(subcat.getProducts(), subcat.getName()));
transaction.addToBackStack(null);
transaction.commit();
}
});
Button catAddButton = (Button) v.findViewById(R.id.catAddButton);
catAddButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "old size gridview : " + gridCat.getChildCount());
subcatList.add(new SubCategory());
Log.d(TAG, "new size list : " + subcatList.size());
adapter.notifyDataSetChanged();
Log.d(TAG, "new size gridview : " + gridCat.getChildCount());
//HERE : childCount is the same !
RelativeLayout rl = (RelativeLayout) gridCat.getChildAt(gridCat.getChildCount()-1);
rl.findViewById(R.id.subcatName).setFocusable(true);
rl.findViewById(R.id.subcatName).setLongClickable(true);
rl.findViewById(R.id.subcatName).requestFocus();
}
});
My Adapter :
public class GridCategoryAdapter extends BaseAdapter {
private static final String TAG = "com.zester.manager.ListViewSizeAndPriceAdapter";
private LayoutInflater mInflater;
private final Context context;
private ArrayList<SubCategory> listSubCat;
private ViewHolder holder;
public GridCategoryAdapter(Context context, ArrayList<SubCategory> values) {
super();
this.context = context;
listSubCat = values;
mInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return listSubCat.size();
}
#Override
public Object getItem(int position) {
return listSubCat.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.subcat_view, null);
holder = new ViewHolder();
holder.SubCatName = (EditText) convertView.findViewById(R.id.subcatName);
holder.imageSubCat = (ImageView) convertView.findViewById(R.id.imageSubCatView);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
SubCategory subCat = (SubCategory) getItem(position);
if (subCat != null) {
holder.SubCatName.setText(subCat.getName());
holder.imageSubCat.setImageDrawable(context.getResources().getDrawable(R.drawable.subcat_default));
}
return convertView;
}
static class ViewHolder {
public EditText SubCatName;
public ImageView imageSubCat;
}
}
XML for each item on the gridview :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="100dp">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:id="#+id/imageSubCatView"
android:src="#drawable/subcat_default"
android:layout_centerHorizontal="true"/>
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/subcatName"
android:gravity="center_horizontal"
android:hint="Ex : Bières"
android:layout_marginTop="20dp"
android:layout_below="#+id/imageSubCatView"
android:background="#android:color/transparent"
android:singleLine="true"
android:focusable="false"
android:longClickable="false"/>
Thx a lot !
When i tried to get my RelativeLayout, null is return : RelativeLayout rl = (RelativeLayout) gridCat.getChildAt(gridCat.getCount()-1);
I think your answer is this:
when you add
subcatList.add(new SubCategory());
adapter.notifyDataSetChanged();
to your code it is not guaranteed that new view for them has been created, because it is possible that your gridView has 100 children and you are just looking at children from 7 to 20 , so new child at index 100 has not yet inflated because the getView is called upon request in order to save memory so when there is no need to show the 100th child, why it must be called? so relativelayout for that child is null because it has not inflated.
in catAddButton listener you must not touch any gridview item, because when the button click happens it first runs your listener then scrolls to the end of gridView so still you have problem, what sholud you do?
in class of SubCategory() put variable that indicates it has not shown for the first time. in getView of gridview each time you want to inflate new items look at that variable (in the list of your SubCategory at item list.get(position)) and for example if it is boolean toggle it to false so that means the new object is going to be seen by user. So in this way each time you are going to inflate the view you know that if it is the first time or not, if it is first time your boolean is true else it has already been false. if it is not first time remove focus else put focus by calling reqesFocuse.
I am new to Android so my question may seem ridiculous but I cant figure it out.
I started creating an app some time ago and using 'Create new Android Activity' usually created a .java and .xml file for it, and everything worked. Now, after update when I use 'Create new Android Activity' it creates .java with class (which now extends ActionBarActivity and not Activity as before) and it adds a fragment_nameofactivity.xml + all things to make it work like internal class extending Fragment...
Now I used to do some ListView display on the page and without a fragment it all works great, but when fragment got introduced I can no longer findViewById(R.id.list_view) if its inside a fragment...
My question is do I need to place my whole functionality inside the class extending Fragment? I tried but it didn't work... Or do I still write all my functionality in the original class and then somehow access the listView in the fragment...
Here is the code:
public class PlayersActivity extends ActionBarActivity {
PlayerDataDatabaseAdapter playerDataHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_players);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
playerDataHelper = new PlayerDataDatabaseAdapter(this);
playerDataHelper.open();
displayPlayersList();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.players, menu);
return true;
}
#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();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_players,
container, false);
return rootView;
}
}
private void displayPlayersList() {
Cursor cursor = playerDataHelper.getAllPlayers();
String [] columns = playerDataHelper.columnsToBind();
int [] to = new int[] {
R.id.player_name,
};
SimpleCursorAdapter dataAdapter = new SimpleCursorAdapter(this, R.layout.fragment_player_details, cursor, columns, to, 0);
ListView listView = (ListView) findViewById(R.id.players_list);
listView.setAdapter(dataAdapter);
listView.setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> listView, View view, int position, long id) {
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
int player_id = cursor.getInt(cursor.getColumnIndexOrThrow("_id"));
Intent intent = new Intent(PlayersActivity.this, EditPlayerActivity.class);
intent.putExtra("PlayerId", player_id);
startActivity(intent);
}
});
}
public void addNewPlayer(View view) {
Intent intent = new Intent(this, AddPlayerActivity.class);
startActivity(intent);
}
}
Fragment_players.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="uk.co.eximage.soccermum.PlayersActivity$PlaceholderFragment" >
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentTop="true"
android:text="#string/players"
android:textAppearance="?android:attr/textAppearanceLarge" />
<Button
android:id="#+id/button1"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal = "true"
android:layout_below="#+id/textView1"
android:onClick="addNewPlayer"
android:text="#string/add_player" />
<ListView
android:id="#+id/players_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/button1"
>
</ListView>
</RelativeLayout>
activity_players.xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="uk.co.eximage.soccermum.PlayersActivity"
tools:ignore="MergeRootFrame" />
Running this returns NullPointerException on the line that tries to get players_list:
ListView listView = (ListView) findViewById(R.id.players_list);
after this listView is null.
What am I doing wrong?
And finally do I need fragments? Maybe I should just remove them and do it the 'old' way with one view per page?
You need to iniaitlize ListView in Fragment
ListView listView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_players,
container, false);
listView = (ListView)rootView. findViewById(R.id.players_list);
playerDataHelper = new PlayerDataDatabaseAdapter(getActivity());
playerDataHelper.open();
displayPlayersList();
The ListView belongs to fragment_players.xml. Move all your code related to fragment in onCreateView.
Edit:
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
ListView listView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_players,
container, false);
listView = (ListView)rootView. findViewById(R.id.players_list);
playerDataHelper = new PlayerDataDatabaseAdapter(getActivity());
playerDataHelper.open();
displayPlayersList();
return rootView;
}
private void displayPlayersList() {
Cursor cursor = playerDataHelper.getAllPlayers();
String [] columns = playerDataHelper.columnsToBind();
int [] to = new int[] {
R.id.player_name,
};
SimpleCursorAdapter dataAdapter = new SimpleCursorAdapter(getActivity(), R.layout.fragment_player_details, cursor, columns, to, 0);
listView.setAdapter(dataAdapter);
listView.setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> listView, View view, int position, long id) {
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
int player_id = cursor.getInt(cursor.getColumnIndexOrThrow("_id"));
Intent intent = new Intent(getActivity(), EditPlayerActivity.class);
intent.putExtra("PlayerId", player_id);
startActivity(intent);
}
});
}
}
Fragments were introduced to better support the tablet form factor. If you don't plan to rearrange your display (ie. show list and detail view together), you don't need fragments and can go the old way.
You should have to initialize Listview from fragment rootView
Either you have to Declare ListView globally and intialize inside onCreateView of Fragment or have to declare View rootView globally and initialize listview by
ListView listView = (ListView) rootView .findViewById(R.id.players_list);
I want my ListView to contain buttons, but setting the button's xml property, onClick="myFunction" and then placing a public void myFunction(android.view.View view) method in the activity causes an NoSuchMethodException (the stack trace is null) to be thrown, as although the onclick listener is there, it doesn't fire myFunction(...) and cause the activity to close.
How do I create a custom Adapter that connects a View.OnClickListener to a button on each row of a ListView?
My ListView is created as follows...
[activity.java content..]
public void myFunction(android.view.View view)
{
//Do stuff
}
[activity.xml content..]
<LinearLayout xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".FrmCustomerDetails" >
<ListView android:id="#+id/LstCustomerDetailsList" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:clickable="true" android:clipChildren="true" android:divider="#null" android:dividerHeight="0dp" android:fastScrollEnabled="true" android:footerDividersEnabled="false" android:headerDividersEnabled="false" android:requiresFadingEdge="vertical" android:smoothScrollbar="true" />
</LinearLayout>
[activity_row_item.xml content..]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="#+id/Llt" android:layout_width="match_parent" android:layout_height="match_parent" >
<Button android:id="#+id/Btn" android:text="Click me" android:onClick="myFunction" />
</LinearLayout>
Here is how to create the custom Adapter, connecting View.OnClickListener to a ListView with a button per row...
1. Create a layout for a typical row
In this case, the row is composed of three view components:
name (EditText)
value (EditText:inputType="numberDecimal")
delete (Button)
Xml
pay_list_item.xml layout is as follows:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<EditText
android:id="#+id/pay_name"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="2"
android:hint="Name" />
<EditText
android:id="#+id/pay_value"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:inputType="numberDecimal"
android:text="0.0" />
<Button
android:id="#+id/pay_removePay"
android:layout_width="100dp"
android:layout_height="fill_parent"
android:text="Remove Pay"
android:onClick="removePayOnClickHandler" />
</LinearLayout>
Note: the button has onClick handler defined in xml layout file, because we want to refer its action to a specific list item.
Doing this means that the handler will be implemented in Activity file and each button will know which list item it belongs to.
2. Create list item adapter
This is the java class that is the controller for pay_list_item.xml.
It keeps references for all of its views, and it also puts these references in tags, extending the ArrayAdapter interface.
The Adapter:
public class PayListAdapter extends ArrayAdapter<Payment> {
private List<Payment> items;
private int layoutResourceId;
private Context context;
public PayListAdapter(Context context, int layoutResourceId, List<Payment> items) {
super(context, layoutResourceId, items);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
PaymentHolder holder = null;
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new PaymentHolder();
holder.Payment = items.get(position);
holder.removePaymentButton = (ImageButton)row.findViewById(R.id.pay_removePay);
holder.removePaymentButton.setTag(holder.Payment);
holder.name = (TextView)row.findViewById(R.id.pay_name);
holder.value = (TextView)row.findViewById(R.id.pay_value);
row.setTag(holder);
setupItem(holder);
return row;
}
private void setupItem(PaymentHolder holder) {
holder.name.setText(holder.Payment.getName());
holder.value.setText(String.valueOf(holder.Payment.getValue()));
}
public static class PaymentHolder {
Payment Payment;
TextView name;
TextView value;
ImageButton removePaymentButton;
}
}
Here we list the Payment class items.
There are three most important elements here:
PayListAdapter constructor: sets some private fields and calls superclass constructor. It also gets the List of Payment objects. Its implementation is obligatory.
PaymentHolder: static class that holds references to all views that I have to set in this list item. I also keep the Payment object that references to this particular item in list. I set it as tag for ImageButton, that will help me to find the Payment item on list, that user wanted to remove
Overriden getView method: called by superclass. Its goal is to return the single List row. We create its fields and setup their values and store them in static holder. Holder then is put in row’s tag element. Note that there is a performance issue, as the row is being recreated each time it is displayed. I used to add some flag in holder like isCreated, and set it to true after row was already created. then you can add if statement and read tag’s holder instead of creating it from scratch.
Payment.java is quite simple as for now and it looks a bit like BasicNameValuePair:
public class Payment implements Serializable {
private String name = "";
private double value = 0;
public Payment(String name, double value) {
this.setName(name);
this.setValue(value);
}
...
}
There are additional gets and sets for each private field not shown.
3. Add ListView to the activity layout xml file
In its simpliest form, it will be enough to add this view to activity layout:
<ListView
android:id="#+id/EnterPays_PaysList"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</ListView>
4. Set up adapter to this list view in Activity Java code
In order to display items in ListView you need to set up its adapter and map it to some other ArrayList of Payment objects (as I am extending an Array adapter here). Here is code that is responsible for binding adapter to editPersonData.getPayments() ArrayList:
PayListAdapter adapter = new PayListAdapter(AddNewPerson.this, R.layout.pay_list_item, editPersonData.getPayments());
ListView PaysListView = (ListView)findViewById(R.id.EnterPays_PaysList);
PaysListView.setAdapter(adapter);
5. Adding / removing items to ListView (and its adapter)
Adapter is handled just like any other ArrayList, so adding new element to it is as simple as:
Payment testPayment = new Payment("Test", 13);
adapter.add(testPayment);
adapter.remove(testPayment);
6. Handle Remove Payment button click event
In an activity’s code, where ListView is displayed, add public method that will handle remove button click action. The method name has to be exactly the same as it was in pay_list_item.xml:
android:onClick="removePayOnClickHandler"
The method body is as follows:
public void removePayOnClickHandler(View v) {
Payment itemToRemove = (Payment)v.getTag();
adapter.remove(itemToRemove);
}
The Payment object was stored in ImageButton’s Tag element. Now it is enough to read it from Tag, and remove this item from the adapter.
7. Incorporate remove confirmation dialog window
Probably you need also make sure that user intentionally pressed the remove button by asking him additional question in confirmation dialog.
Dialogue
a) Create dialog’s id constant
This is simply dialog’s ID. it should be unique among any other dialog window that is handled by current activity. I set it like that:
protected static final int DIALOG_REMOVE_CALC = 1;
protected static final int DIALOG_REMOVE_PERSON = 2;
b) Build dialog
I use this method to build dialog window:
private Dialog createDialogRemoveConfirm(final int dialogRemove) {
return new AlertDialog.Builder(getApplicationContext())
.setIcon(R.drawable.trashbin_icon)
.setTitle(R.string.calculation_dialog_remove_text)
.setPositiveButton(R.string.calculation_dialog_button_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
handleRemoveConfirm(dialogRemove);
}
})
.setNegativeButton(R.string.calculation_dialog_button_cancel, null)
.create();
}
AlertDialog builder pattern is utilized here. I do not handle NegativeButton click action – by default the dialog is just being hidden. If dialog’s confirm button is clicked, my handleRemoveConfirm callback is called and action is performed based on dialog’s ID:
protected void handleRemoveConfirm(int dialogType) {
if(dialogType == DIALOG_REMOVE_PERSON){
calc.removePerson();
}else if(dialogType == DIALOG_REMOVE_CALC){
removeCalc();
}
}
c) Show Dialog
I show dialog after my remove button click. The showDialog(int) is Android’s Activity’s method:
OnClickListener removeCalcButtonClickListener = new OnClickListener() {
public void onClick(View v) {
showDialog(DIALOG_REMOVE_CALC);
}
};
the showDialog(int) method calls onCreateDialog (also defined in Activity’s class). Override it and tell your app what to do if the showDialog was requested:
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_REMOVE_CALC:
return createDialogRemoveConfirm(DIALOG_REMOVE_CALC);
case DIALOG_REMOVE_PERSON:
return createDialogRemoveConfirm(DIALOG_REMOVE_PERSON);
}
}
Take a look at this blog post I wrote on exactly this matter:
Create custom ArrayAdapter
There are comments that explain every action I make in the adapter.
Here is the explanation in short:
So lets for example take a row where you want to place a CheckBox, ImageView
and a TextView while all of them are clickable. Meaning that you can click the
row it self for going to another Actvity for more details on the row, check its
CheckBox or press the ImageView to perform another operation.
So what you should do is:
1. First create an XML layout file for your ListView row:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<CheckBox
android:id="#+id/cbCheckListItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp" />
<TextView
android:id="#+id/tvItemTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="item string" />
<ImageView
android:id="#+id/iStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:contentDescription="#drawable/ic_launcher"
android:src="#drawable/ic_launcher" />
</LinearLayout>
2. Second in your java code define a ViewHolder, a ViewHolder
is designed to hold the row views and that way operating more quickly:
static class ViewHolder
{
TextView title;
CheckBox checked;
ImageView changeRowStatus;
}
3. Now we have to define CustomArrayAdapter, using the array adapter
we can define precisely what is the desired output for each row based on the content of this
row or it’s position. We can do so by overriding the getView method:
private class CustomArrayAdapter extends ArrayAdapter<RowData>
{
private ArrayList<RowData> list;
//this custom adapter receives an ArrayList of RowData objects.
//RowData is my class that represents the data for a single row and could be anything.
public CustomArrayAdapter(Context context, int textViewResourceId, ArrayList<RowData> rowDataList)
{
//populate the local list with data.
super(context, textViewResourceId, rowDataList);
this.list = new ArrayList<RowData>();
this.list.addAll(rowDataList);
}
public View getView(final int position, View convertView, ViewGroup parent)
{
//creating the ViewHolder we defined earlier.
ViewHolder holder = new ViewHolder();)
//creating LayoutInflator for inflating the row layout.
LayoutInflater inflator = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//inflating the row layout we defined earlier.
convertView = inflator.inflate(R.layout.row_item_layout, null);
//setting the views into the ViewHolder.
holder.title = (TextView) convertView.findViewById(R.id.tvItemTitle);
holder.changeRowStatus = (ImageView) convertView.findViewById(R.id.iStatus);
holder.changeRowStatus.setTag(position);
//define an onClickListener for the ImageView.
holder.changeRowStatus.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
Toast.makeText(activity, "Image from row " + position + " was pressed", Toast.LENGTH_LONG).show();
}
});
holder.checked = (CheckBox) convertView.findViewById(R.id.cbCheckListItem);
holder.checked.setTag(position);
//define an onClickListener for the CheckBox.
holder.checked.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
//assign check-box state to the corresponding object in list.
CheckBox checkbox = (CheckBox) v;
rowDataList.get(position).setChecked(checkbox.isChecked());
Toast.makeText(activity, "CheckBox from row " + position + " was checked", Toast.LENGTH_LONG).show();
}
});
//setting data into the the ViewHolder.
holder.title.setText(RowData.getName());
holder.checked.setChecked(RowData.isChecked());
//return the row view.
return convertView;
}
}
4. Now you need to set this adapter, as the adapter of your ListView.
this ListView can be created in java or using an XML file, in this case I’m using a list that was
defined in the XML file using the “list” id:
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
ListView list = (ListView)findViewById(R.id.list);
CustomArrayAdapter dataAdapter = new CustomArrayAdapter(this, R.id.tvItemTitle, rowDataList);
list.setAdapter(dataAdapter);
}
5. Finally if we want to be able to press the row it self and not only a certain view in it
we should assign an onItemClickListener to the ListView:
list.setOnItemClickListener(new OnItemClickListener()
{
public void onItemClick(AdapterView<?> parent, View view,int position, long id)
{
Toast.makeText(activity, "row " + position + " was pressed", Toast.LENGTH_LONG).show();
}
});
First, the way of adding listeners in xml using onClick="function" is deprecated. You need a ViewHolder class to link the button in the xml to your java code. Then you can implement onClickListener for that.
Inside your getView() implementation of CustomAdapter, you can try like below.
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.xxxxx, null);
holder = new ViewHolder();
holder.invite = (Button) convertView.findViewById(R.id.button);
} else {
holder = (ViewHolder) convertView.getTag();
}
final int pos = position;
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
handleClick(pos);
}
});
}
class ViewHolder {
Button button;
}