I'm trying to set up a 2-line list view but each string seems to copy itself within the item rather than showing it once. How can I prevent this from happening? i.e. I need the data to appear like this: for Item 1 - America, America Description rather than America, America; for Item 2 - Europe, Europe Description rather than Europe, Europe. See screenshot for evidence of the undesired result.
ListData.java
public class ListData {
public static final int[][] items = {
{R.string.america,R.string.america_description},
{R.string.europe, R.string.europe_description}
};
}
ListViewAdapter
public class ItemListAdapter extends BaseAdapter implements Filterable {
private List<String> mData;
private List<String> mFilteredData;
private LayoutInflater mInflater;
private ItemFilter mFilter;
public ItemListAdapter (List<String> data, Context context) {
mData = data;
mFilteredData = data;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mFilteredData.size();
}
#Override
public String getItem(int position) {
return mFilteredData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
String strItem = mFilteredData.get(position);
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
holder = new ViewHolder();
holder.mTitle = (TextView) convertView.findViewById(R.id.item_title);
holder.mDescription = (TextView) convertView.findViewById(R.id.item_description);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.mTitle.setText(strItem);
holder.mDescription.setText(strItem);
return convertView;
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ItemFilter();
}
return mFilter;
}
/**
* View holder
*/
static class ViewHolder {
private TextView mTitle;
private TextView mDescription;
}
}
item_row.xml
<?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="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingRight="?android:attr/scrollbarSize"
android:baselineAligned="false">
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView android:id="#+id/item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:ellipsize="marquee"
android:textColor="?android:attr/textColorPrimary" />
<TextView android:id="#+id/item_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/item_title"
android:layout_alignLeft="#id/item_title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary" />
</RelativeLayout>
</LinearLayout>
ItemListadapter.java
public class ItemListAdapter extends BaseAdapter implements Filterable {
private List<String> mData;
private List<String> mFilteredData;
private LayoutInflater mInflater;
private ItemFilter mFilter;
public ItemListAdapter (List<String> data, Context context) {
mData = data;
mFilteredData = data;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mFilteredData.size();
}
#Override
public String getItem(int position) {
return mFilteredData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
String strItem = mFilteredData.get(position);
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
holder = new ViewHolder();
holder.mTitle = (TextView) convertView.findViewById(R.id.item_title);
holder.mDescription = (TextView) convertView.findViewById(R.id.item_description);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.mTitle.setText(strItem);
holder.mDescription.setText(strItem);
return convertView;
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ItemFilter();
}
return mFilter;
}
/**
* View holder
*/
static class ViewHolder {
private TextView mTitle;
private TextView mDescription;
}
/**
* Filter for filtering list items
*/
private class ItemFilter extends Filter {
/**
* Invoked on a background thread. This is where all the filter logic should go
* #param constraint the constraint to filter on
* #return the resulting list after applying the constraint
*/
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (TextUtils.isEmpty(constraint)) {
results.count = mData.size();
results.values = mData;
} else {
//Create a new list to filter on
List<String> resultList = new ArrayList<>();
for (String str : mData) {
if (str.toLowerCase().contains(constraint.toString().toLowerCase())) {
resultList.add(str);
}
}
results.count = resultList.size();
results.values = resultList;
}
return results;
}
/**
* Runs on ui thread
* #param constraint the constraint used for the result
* #param results the results to display
*/
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
notifyDataSetInvalidated();
} else {
mFilteredData = (ArrayList<String>)results.values;
notifyDataSetChanged();
}
}
}
}
The name is shown two times in ListView because you are passing in both TextView the same item:
String strItem = mFilteredData.get(position);
To avoid this and use two separate fields you need to pass to your adapter a model with two field:
public class CustomListItem {
private String title;
private String description;
//get method
public CustomListItem(String title, String description){
this.title = title;
this.description = description;
}
}
Then you have to add them to create a new List<CustomListItem> in your activity/fragment that use the adapter and pass to the adapter this list:
public class ItemListAdapter extends ArrayAdapter<CustomListItem>{
private Context context;
private List<CustomListItem> data;
public ItemListAdapter(Context context, List<CustomListItem> data){
this.context = context;
this.data = data;
}
}
Related
I create a adapter :
public class Adapter extends BaseAdapter implements Filterable {
Context context;
ArrayList<RowBean> data = null;
private LayoutInflater inflater;
private ValueFilter valueFilter;
private ArrayList<RowBean> mStringFilterList;
public Adapter(Context context, ArrayList<RowBean> data) {
super();
this.context = context;
this.data = data;
this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mStringFilterList = data;
getFilter();
}
#Override
public int getCount() {
return data.size();
}
#Override
public Object getItem(int position) {
return data.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
RowBeanHolder holder;
RowBean rowBean = data.get(position);
if(convertView == null){
holder = new RowBeanHolder();
convertView = inflater.inflate(R.layout.all_object_holder,null);
holder.txtTitle = (TextView) convertView.findViewById(R.id.showText);
holder.selected = (CheckBox) convertView.findViewById(R.id.checkBox12);
}else{
holder = (RowBeanHolder) convertView.getTag();
}
if(rowBean != null) {
holder.txtTitle.setText(rowBean.getTitle());
holder.selected.setChecked(rowBean.isSelected());
}
return convertView;
}
#Override
public Filter getFilter() {
if(valueFilter==null) {
valueFilter=new ValueFilter();
}
return valueFilter;
}
private class RowBeanHolder {
TextView txtTitle;
CheckBox selected;
}
private class ValueFilter extends Filter {
//Invoked in a worker thread to filter the data according to the constraint.
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results=new FilterResults();
if(constraint!=null && constraint.length()>0){
ArrayList<RowBean> filterList=new ArrayList<>();
for(int i=0;i<mStringFilterList.size();i++){
if((mStringFilterList.get(i).getTitle().toUpperCase())
.contains(constraint.toString().toUpperCase())) {
RowBean contacts = new RowBean();
contacts.setTitle(mStringFilterList.get(i).getTitle());
contacts.setSelected(mStringFilterList.get(i).isSelected());
filterList.add(contacts);
}
}
results.count=filterList.size();
results.values=filterList;
}else{
results.count=mStringFilterList.size();
results.values=mStringFilterList;
}
return results;
}
//Invoked in the UI thread to publish the filtering results in the user interface.
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
data=(ArrayList<RowBean>) results.values;
notifyDataSetChanged();
}
}
}
When I start my activity with this adapter I log I see this :
Process: com.maps, PID: 20174
java.lang.NullPointerException: Attempt to read from field 'android.widget.TextView com.maps.Adapter.Adapter$RowBeanHolder.txtTitle' on a null object reference
this is a line :
holder.txtTitle.setText(rowBean.getTitle());
This is a layout all_object_holder:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox
android:id="#+id/checkBox12"
android:layout_width="50dp"
android:layout_height="50dp"
android:clickable="false"
android:button="#xml/setting_checkbox"
android:focusable="false"
android:gravity="center" />
<TextView
android:id="#+id/showText"
android:layout_width="395dp"
android:layout_height="wrap_content"
android:text="#string/show_choose_point"
android:textColor="#000000"
android:layout_alignBaseline="#+id/checkBox12"
android:layout_alignBottom="#+id/checkBox12"
android:layout_toRightOf="#+id/checkBox12"
android:layout_toEndOf="#+id/checkBox12" />
</RelativeLayout>
Add line
convertView.setTag(holder);
without it convertView.getTag() will return null.
if(convertView == null){
holder = new RowBeanHolder();
convertView = inflater.inflate(R.layout.all_object_holder,null);
holder.txtTitle = (TextView) convertView.findViewById(R.id.showText);
holder.selected = (CheckBox)
convertView.setTag(holder);
}
You didn't add convertView.setTag(holder). Please add it to your code and check.
NullPointerException occurs because your holder object is null sometimes. You should setTag of your convertView, just add convertView.setTag(holder); method to getView method like that:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
RowBeanHolder holder;
RowBean rowBean = data.get(position);
if(convertView == null){
holder = new RowBeanHolder();
convertView = inflater.inflate(R.layout.all_object_holder,null);
holder.txtTitle = (TextView) convertView.findViewById(R.id.showText);
holder.selected = (CheckBox) convertView.findViewById(R.id.checkBox12);
convertView.setTag(holder);
}else{
holder = (RowBeanHolder) convertView.getTag();
}
if(rowBean != null) {
holder.txtTitle.setText(rowBean.getTitle());
holder.selected.setChecked(rowBean.isSelected());
}
return convertView;
}
Good luck.
Update your code with this code
#Override
public View getView(int position, View convertView, ViewGroup parent) {
RowBeanHolder holder = new RowBeanHolder();
RowBean rowBean = data.get(position);
if(convertView == null){
convertView = inflater.inflate(R.layout.all_object_holder,null);
}
holder.txtTitle = (TextView) convertView.findViewById(R.id.showText);
holder.selected = (CheckBox) convertView.findViewById(R.id.checkBox12);
if(rowBean != null) {
holder.txtTitle.setText(rowBean.getTitle());
holder.selected.setChecked(rowBean.isSelected());
}
return convertView;
}
I want to mark radio button true in listview onItemClick so what I am doing is
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
LinearLayout item_view = (LinearLayout) view;
final RadioButton itemcheck = (RadioButton) item_view
.findViewById(R.id.listview_radiobutton);
if (itemcheck.isChecked()) {
itemcheck.setChecked(true);
} else {
itemcheck.setChecked(false);
}
itemcheck.setChecked(true);
}
My listview
<ListView
android:id="#+id/listview"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_marginLeft="#dimen/view_margin_15"
android:layout_marginRight="#dimen/view_margin_15"
android:layout_marginTop="#dimen/view_margin_20"
android:layout_weight="1"
android:choiceMode="singleChoice"
android:divider="#drawable/list_divider"
android:dividerHeight="#dimen/padding_2"
android:fastScrollEnabled="true"
android:footerDividersEnabled="true"
android:listSelector="#null"
android:scrollbars="none"
android:textFilterEnabled="true"
android:textStyle="normal" />
Edit:--
My Adapter code :--
public class Adapter extends ArrayAdapter<Details> {
private Context mContext;
private List<Details> transList;
private LayoutInflater infalInflater;
private OnCheckedRadioButon onCheckedRadioButon;
private Typeface mTypeface, mEditTypeface, mPasswdTypeface;
private int mSelectedPosition = -1;
private RadioButton mSelectedRB;
private PromoViewHolder viewHolder;
public Adapter(Context context, List<Details> mtransList, OnCheckedRadioButon onCheckedRadioButon) {
super(context, R.layout.dialog_listview, mtransList);
this.mContext = context;
this.transList = mtransList;
this.onCheckedRadioButon = onCheckedRadioButon;
this.infalInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View row = convertView;
try {
viewHolder = null;
row = infalInflater.inflate(R.layout.dialog_listview_code, parent, false);
viewHolder = new ViewHolder();
viewHolder.radiobutton = (RadioButton) row.findViewById(R.id.radiobutton);
viewHolder.listview_name = (TextView) row.findViewById(R.id.listview_name);
setValueText(viewHolder, position);
viewHolder.radiobutton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
if (checked) {
onCheckedRadioButon.onCheckedButton(transList.get(position));
}
}
});
viewHolder.radiobutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (position != mSelectedPosition && mSelectedRB != null) {
mSelectedRB.setChecked(false);
}
mSelectedPosition = position;
mSelectedRB = (RadioButton) v;
}
});
if (mSelectedPosition != position) {
viewHolder.radiobutton.setChecked(false);
} else {
viewHolder.radiobutton.setChecked(true);
if (mSelectedRB != null && viewHolder.radiobutton != mSelectedRB) {
mSelectedRB = viewHolder.radiobutton;
}
}
row.setTag(viewHolder);
} catch (Exception e) {
e.printStackTrace();
}
return row;
}
private void setValueText(ViewHolder viewHolder, final int position) {
viewHolder.listview_name.setText(transList.get(position).getName());
}
public interface OnCheckedRadioButon {
void onCheckedButton(Details pr);
}
class ViewHolder {
RadioButton radiobutton;
TextView listview_name;
}
}
It is working but if I click on another position of the listview then the previous radiobutton position is not unchecked.I want to uncheck all the previous ones and mark only one at a time.
Any help will be appreciated.
Use POJO classes (Setter or Getter) to manage this type of condition. Use boolean variable in that class and change its values according to the position true or false.
POJO Class Like :
public class CheckListSource {
public boolean isSelected;
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
}
In your adapter :
private ArrayList<CheckListSource > itemsData;
public ChildListAdapter(Activity activity, ArrayList<ChildListResponse> baseResponse) {
this.itemsData = baseResponse;
this.activity = activity;
}
In BindViewHolder Like :
viewHolder.checkParentView.setTag(itemsData.get(position));
viewHolder.checkParentView.setOnClickListener(checkedListener);
private View.OnClickListener checkedListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
CheckListSource childListResponse = (CheckListSource ) v.getTag();
if (childListResponse.isSelected())
childListResponse.setSelected(false);
else
childListResponse.setSelected(true);
notifyDataSetChanged();
}
};
Use a boolean array in your Activity. Each boolean value corresponds to a RadioButton.
If a RadioButton is checked, set its boolean value to true, and set all other boolean values in the array to false.
In the getView() of your Adapter, call your_radio_button.setChecked(your_boolean_array[position]).
Once the boolean array is modified, call your_adapter.notifyDataSetChanged().
Checkout this it works for me..
public class ProgramAdapter extends ArrayAdapter<KeyInformation> {
private final String TAG = "ProgramAdapter";
private List<KeyInformation> mList;
private Context mContext;
private LayoutInflater mInflater;
private int mSelectedPosition = -1;
private RadioButton mSelectedRB;
private String mUserApllication = "";
public ProgramAdapter(Context context, List<KeyInformation> objects) {
super(context, R.layout.program_item, objects);
mContext = context;
mList = objects;
mInflater = ( LayoutInflater ) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mUserApllication = Settings.System.getString(mContext.getContentResolver(),
Settings.System.PROGRAMMABLE_KEY_ACTION);
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.program_item, parent, false);
holder.mImageView = (ImageView) convertView.findViewById(R.id.icon);
holder.mTextView = (TextView) convertView.findViewById(R.id.text);
holder.mSeparator = (TextView) convertView.findViewById(R.id.title_separator);
holder.mRadioButton = (RadioButton) convertView.findViewById(R.id.radioBtn);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.mRadioButton.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View view) {
if(position != mSelectedPosition && mSelectedRB != null){
mSelectedRB.setChecked(false);
}
mUserApllication ="";
mSelectedPosition = position;
mSelectedRB = (RadioButton) view;
}
});
String userApp = mList.get(position).packageName;
if(mUserApllication.equals(userApp)) {
mSelectedPosition = position;
}
if (mSelectedPosition != position) {
holder.mRadioButton.setChecked(false);
} else {
holder.mRadioButton.setChecked(true);
mSelectedRB = holder.mRadioButton;
}
holder.mImageView.setImageDrawable(mList.get(position).icon);
holder.mTextView.setText(mList.get(position).lable);
if (position == 5) {
holder.mSeparator.setVisibility(View.VISIBLE);
} else {
holder.mSeparator.setVisibility(View.GONE);
}
return convertView;
}
public int getmSelectedPosition () {
return mSelectedPosition;
}
private static class ViewHolder {
ImageView mImageView;
TextView mTextView;
TextView mSeparator;
RadioButton mRadioButton;
}
}
Please go through below, it will work.
class Details{
public boolean isSelect=false;
}
public class Adapter extends ArrayAdapter<Details> {
private Context mContext;
private List<Details> transList;
private LayoutInflater infalInflater;
private OnCheckedRadioButon onCheckedRadioButon;
private Typeface mTypeface, mEditTypeface, mPasswdTypeface;
private int mSelectedPosition = -1;
private RadioButton mSelectedRB;
private PromoViewHolder viewHolder;
public Adapter(Context context, List<Details> mtransList, OnCheckedRadioButon onCheckedRadioButon) {
super(context, R.layout.dialog_listview, mtransList);
this.mContext = context;
this.transList = mtransList;
this.onCheckedRadioButon = onCheckedRadioButon;
this.infalInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void updateList(){
viewHolder.radiobutton.setChecked(false);
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View row = convertView;
try {
viewHolder = null;
row = infalInflater.inflate(R.layout.dialog_listview_code, parent, false);
viewHolder = new ViewHolder();
viewHolder.radiobutton = (RadioButton) row.findViewById(R.id.radiobutton);
viewHolder.listview_name = (TextView) row.findViewById(R.id.listview_name);
setValueText(viewHolder, position);
viewHolder.radiobutton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
if (checked) {
onCheckedRadioButon.onCheckedButton(transList.get(position));
}
}
});
viewHolder.radiobutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Details detail=transList.get(position);
for(int i=0;i<transList.size;i++){
Detail b=transList.get(i);
b.isSelect=false;
}
detail.isSelect=true;
adapter.notifydatasetchange();
}
});
Details detail=transList.get(position);
if (detail.isSelect) {
viewHolder.radiobutton.setChecked(true);
} else {
viewHolder.radiobutton.setChecked(false);
}
row.setTag(viewHolder);
} catch (Exception e) {
e.printStackTrace();
}
return row;
}
private void setValueText(ViewHolder viewHolder, final int position) {
viewHolder.listview_name.setText(transList.get(position).getName());
}
public interface OnCheckedRadioButon {
void onCheckedButton(PromoDetails promoDetails);
}
class ViewHolder {
RadioButton radiobutton;
TextView listview_name;
}
}
I've created a list view with filtering that operates well. However I was just wondering, how can I get my list view to remove items that don't begin with the supplied prefix as I type into my search view? As seen in the supplied screenshot, when I type in the letter 'p' none of my items begin with that letter but yet the list doesn't disappear.
public class ItemListAdapter extends BaseAdapter implements Filterable {
private List<Victoria> mData;
private List<Victoria> mFilteredData;
private LayoutInflater mInflater;
private ItemFilter mFilter;
public ItemListAdapter (List<Victoria> data, Context context) {
mData = data;
mFilteredData = new ArrayList(mData);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mFilteredData.size();
}
#Override
public String getItem(int position) {
return mFilteredData.get(position).getItem();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.item_title);
holder.description = (TextView) convertView.findViewById(R.id.item_description);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.title.setText(mFilteredData.get(position).getItem());
holder.description.setText(mFilteredData.get(position).getItemDescription());
return convertView;
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ItemFilter();
}
return mFilter;
}
static class ViewHolder {
private TextView title;
private TextView description;
}
private class ItemFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (TextUtils.isEmpty(constraint)) {
results.count = mData.size();
results.values = new ArrayList(mData);
} else {
List<Victoria> resultList = new ArrayList<Victoria>();
for (Victoria str : mData) {
if (str.getItemDescription().toLowerCase().contains(constraint.toString().toLowerCase())) {
resultList.add(str);
}
}
results.count = resultList.size();
results.values = resultList;
}
return results;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
mFilteredData.clear();
notifyDataSetInvalidated();
} else {
mFilteredData = (ArrayList<Victoria>)results.values;
notifyDataSetChanged();
}
}
}
}
I'm trying to get my list to show all my items again whenever I cancel a search from my search view but for some strange reason, the list gets stuck with the results only from the previous search. Does anyone know what is wrong with my code and how to fix this? I believe something is wrong with the filter related code but I don't know what it is.
FilterListFragment.java
public class ItemListAdapter extends BaseAdapter implements Filterable {
private List<Item> mData;
private List<Item> mFilteredData;
private LayoutInflater mInflater;
private ItemFilter mFilter;
public ItemListAdapter (List<Item> data, Context context) {
mData = data;
mFilteredData = data;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mFilteredData.size();
}
#Override
public String getItem(int position) {
return mFilteredData.get(position).getItem();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.item_title);
holder.description = (TextView) convertView.findViewById(R.id.item_description);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.title.setText(mData.get(position).getItem());
holder.description.setText(mData.get(position).getItemDescription());
return convertView;
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ItemFilter();
}
return mFilter;
}
/**
* View holder
*/
static class ViewHolder {
private TextView title;
private TextView description;
}
/**
* Filter for filtering list items
*/
private class ItemFilter extends Filter {
/**
* Invoked on a background thread. This is where all the filter logic should go
* #param constraint the constraint to filter on
* #return the resulting list after applying the constraint
*/
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (TextUtils.isEmpty(constraint)) {
results.count = mData.size();
results.values = mData;
} else {
//Create a new list to filter on
List<Item> resultList = new ArrayList<Item>();
for (Item str : mData) {
if (str.getItem().toLowerCase().contains(constraint.toString().toLowerCase())) {
resultList.add(str);
}
}
results.count = resultList.size();
results.values = resultList;
}
return results;
}
/**
* Runs on ui thread
* #param constraint the constraint used for the result
* #param results the results to display
*/
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
notifyDataSetInvalidated();
} else {
mFilteredData = (ArrayList<Item>)results.values;
notifyDataSetChanged();
}
}
}
}
List in normal state
List in filtered state
You are operating on the original data instead of filtered data. You should maintain a reference to original data and use the filtered data for all other purposes. So that the original data is displayed when search is cleared.
Replace all usages of mData with mFilteredData as below and only use the original data to generate the filtered data:
private List<String> mData;
private List<String> mFilteredData;
private LayoutInflater mInflater;
private ItemFilter mFilter;
public ItemListAdapter (List<String> data, Context context) {
mData = data;
mFilteredData = data;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mFilteredData.size();
}
#Override
public String getItem(int position) {
return mFilteredData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
String strItem = mFilteredData.get(position);
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
holder = new ViewHolder();
holder.mTvItem = (TextView) convertView.findViewById(R.id.tv_item);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.mTvItem.setText(strItem);
return convertView;
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ItemFilter();
}
return mFilter;
}
/**
* View holder
*/
static class ViewHolder {
private TextView mTvItem;
}
/**
* Filter for filtering list items
*/
private class ItemFilter extends Filter {
/**
* Invoked on a background thread. This is where all the filter logic should go
* #param constraint the constraint to filter on
* #return the resulting list after applying the constraint
*/
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (TextUtils.isEmpty(constraint)) {
results.count = mData.size();
results.values = mData;
} else {
//Create a new list to filter on
List<String> resultList = new ArrayList<>();
for (String str : mData) {
if (str.toLowerCase().contains(constraint.toString().toLowerCase())) {
resultList.add(str);
}
}
results.count = resultList.size();
results.values = resultList;
}
return results;
}
/**
* Runs on ui thread
* #param constraint the constraint used for the result
* #param results the results to display
*/
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
notifyDataSetInvalidated();
} else {
mFilteredData = (ArrayList<String>)results.values;
notifyDataSetChanged();
}
}
}
Add a log statement to onQueryTextChange() so you can see how your filter string is changing. Note that SearchView does not call onQueryTextChange() with an empty string when the search view is closed. You need to decide when you want the search string cleared, perhaps by adding a control for that, and do the clearing yourself.
This is a simple listview example
public class Test extends ListActivity {
ArrayList<String> txt;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
txt=new ArrayList<String>();
txt.add("diaplay text 1");
txt.add("diaplay text 2");
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, txt));
}
}
But this can only store string
I want do something like this
<ul>
<li data-meta="hidden text 1">display text 1</li>
<li data-meta="hidden text 2">display text 2</li>
</ul>
Because I want store more information in each list item
So I want store this class into listview
public class Item
{
public String displayText;
public String meta;
}
How can I do this?
You have to implement a custom Adapter for that. First we have to fix your view model, you call it Item:
public class Item
{
public String displayText;
public String meta;
}
Those fields should be private with appropriate getters and setters and constructors. If you want to modify such Items in a List you also need to implement equals() and hashCode(). If you do all that your Item class should look something like this:
public class Item {
private String displayText;
private String meta;
public Item(String displayText, String meta) {
this.displayText = displayText;
this.meta = meta;
}
public Item() {
}
public String getDisplayText() {
return displayText;
}
public void setDisplayText(String displayText) {
this.displayText = displayText;
}
public String getMeta() {
return meta;
}
public void setMeta(String meta) {
this.meta = meta;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Item item = (Item) o;
if (displayText != null ? !displayText.equals(item.displayText) : item.displayText != null) return false;
return !(meta != null ? !meta.equals(item.meta) : item.meta != null);
}
#Override
public int hashCode() {
int result = displayText != null ? displayText.hashCode() : 0;
result = 31 * result + (meta != null ? meta.hashCode() : 0);
return result;
}
}
Now we need to create a layout for the Items in the ListView, for example something simple like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rlRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:clickable="true"
android:background="#drawable/list_item_background">
<TextView
style="#style/DefaultTextView"
android:id="#+id/tvDisplayText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true" />
<TextView
style="#style/DefaultTextView"
android:id="#+id/tvMeta"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="#id/tvDisplayText"
android:layout_toStartOf="#id/tvDisplayText"
android:layout_centerVertical="true" />
</RelativeLayout>
In this layout we have two TextViews to display both the Strings from each Item. To increase ListView performance you should always implement the view holder pattern. For this purpose we create a ViewHolder class. Its purpose is to hold a reference to the relevant Views in each row of the ListView so we don't have to perform the expensive findViewById() as often:
public class ViewHolder {
public TextView tvDisplayText;
public TextView tvMeta;
}
Note that we don't need getters and setters or anything in this view holder. We will access the public fields directly.
Now we can implement our custom Adapter. This is actually pretty straight forward, I will comment the most important parts.
public class ExampleAdapter extends BaseAdapter {
private final LayoutInflater inflater;
private final List<Item> items;
private ExampleAdapter(Context context, List<Item> items) {
this.inflater = LayoutInflater.from(context);
this.items = items;
}
#Override
public int getCount() {
return this.items.size();
}
#Override
public Item getItem(int position) {
return this.items.get(position);
}
#Override
public long getItemId(int position) {
return this.items.get(position).hashCode();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final Item item = getItem(position);
if(convertView == null) {
// If convertView is null we have to inflate a new layout
convertView = this.inflater.inflate(R.layout.example_list_item, parent, false);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.tvDisplayText = (TextView) convertView.findViewById(R.id.tvDisplayText);
viewHolder.tvMeta = (TextView) convertView.findViewById(R.id.tvMeta);
// We set the view holder as tag of the convertView so we can access the view holder later on.
convertView.setTag(viewHolder);
}
// Retrieve the view holder from the convertView
final ViewHolder viewHolder = (ViewHolder) convertView.getTag();
// Bind the values to the views
viewHolder.tvDisplayText.setText(item.getDisplayText());
viewHolder.tvMeta.setText(item.getMeta());
return convertView;
}
}
And you would use this custom Adapter like this:
final List<Item> items = new ArrayList<Item>();
items.add(new Item("a", "b"));
items.add(new Item("c", "d"));
items.add(new Item("e", "f"));
items.add(new Item("g", "h"));
final ExampleAdapter adapter = new ExampleAdapter(context, items);
listView.setAdapter(adapter);
You have implementation BaseAdapter, ArrayApdater or somethings.
In your adapter you pass a list of custom objects.
Here a sample code:
public class CustomListAdapter extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private List<Item> items;
public CustomListAdapter(Activity activity, List<Item> items) {
this.activity = activity;
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int location) {
return items.get(location);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (inflater == null)
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null)
convertView = inflater.inflate(R.layout.yourlayout, null);
Item item = items.get(position);
//Setter text
return convertView;
}
}
Tutorial:
BaseAdapter
Official Documentation
To store custom data in a ListView adapter, you must implement your own. Take a look at Custom BaseAdapter with ListView.
I don't know clearly understand your question. But i understand that. Hope for help.
strings.xml
<string-array name="li_items">
<item >Home</item>
<item >Find People</item>
<item >Photos</item>
<item >Communities</item>
<item >Pages</item>
</string-array>
In your ListActivity
public class Test extends ListActivity {
ArrayList<String> txt;
private String[] liItem;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
liItem= getResources().getStringArray(R.array.li_items);
txt=new ArrayList<String>();
txt.add(liItem[0]);
txt.add(liItem[1]);
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, txt));
}
}