SpannableString regex in a ListView - android

I have a ListView that I'm binding a collection of strings to, using a custom adapter. I'm also underlining certain keywords in the text. I'm using a SpannableString and a regular expression to underline the words, but I'm wondering if this is the most efficient way to do it? I'm noticing a lot of allocations in the Allocation Tracker of the java.util.regex.Matcher and the regex.util.regex.Pattern classes, which may be causing memory leaks in my app. I know regex's can be expensive, but I'm not sure another way to do what I need to do.
public class Main extends ListActivity
{
private static CustomAdapter adapter = null;
private static List<Keyword> keywords;
private static Matcher matcher;
#Override
public void onCreate(Bundle icicle)
{
List<Item> items = new ArrayList<Item>();
keywords = GetKeywords();
items = GetItems();
adapter = new CustomAdapter();
for (Item item : items)
adapter.addItem(item);
this.setListAdapter(adapter);
adapter.notifyDataSetChanged();
}
/* ADAPTER */
private class CustomAdapter extends BaseAdapter
{
private final List<Item> mData = new ArrayList<Item>();
private final LayoutInflater mInflater;
public CustomAdapter() {
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void addItem(Item item) {
mData.add(item);
}
#Override
public int getCount() {
return mData.size();
}
#Override
public Object getItem(int position) {
return mData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
final ViewHolder holder;
final Item item = (Item)this.getItem(position);
if (convertView == null)
{
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.main, parent, false);
holder.text = (TextView)convertView.findViewById(R.id.text);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder)convertView.getTag();
}
holder.text.setText(Highlight(item.getTitle(), keywords, matcher), BufferType.SPANNABLE);
return(convertView);
}
}
static class ViewHolder {
TextView text, date, site;
}
private SpannableString Highlight(String text, List<Keyword> keywords, Matcher matcher)
{
final SpannableString content = new SpannableString(text);
for (Keyword keyword : keywords)
{
matcher = Pattern.compile("\\b" + keyword + "\\b").matcher(text);
if (matcher.find())
{
start = matcher.start();
end = matcher.end();
content.setSpan(new UnderlineSpan(), start, end, 0);
}
}
}
return content;
}
}

You are creating a lot of Patterns and Matchers you don't need. I suggest you create one regex to match all the keywords, like this:
private SpannableString Highlight(String text, List<Keyword> keywords)
{
final SpannableString content = new SpannableString(text);
if (keywords.size() > 0)
{
/* create a regex of the form: \b(?:word1|word2|word3)\b */
StringBuilder sb = ne StringBuilder("\\b(?:").append(keywords.get(0).toString());
for (int i = 1; i < keywords.size(); i++)
{
sb.append("|").append(keywords.get(i).toString());
}
sb.append(")\\b");
Matcher m = Pattern.compile(sb.toString()).matcher(text);
while (m.find())
{
content.setSpan(new UnderlineSpan(), m.start(), m.end(), 0);
}
}
return content;
}
Pattern objects are quite expensive to create, so that's where your real savings will come from. On the other hand, Matchers are relatively cheap, which is why I switched from using a static instance to creating a new one each time.

Related

searchview result in listview, but not highlight the result

I have created a searchview, when i type a word and press ENTER from the keyboard, the results show on a listview in 3 or 4 seconds. so i want to insert a progres spinner till the result populated. And Also I want to try to highlight the search word in the result in list view. I use "SpannableString highlightKeyword" but unable to highlight the result search word, I have tried many ways followed by several websites, but nothing happend. I Couldn't figure out where the mistake. Here are My codes:
mainactivity.java :
public class MainActivity extends AppCompatActivity implements SearchView.OnQueryTextListener {
// Declare Variables
ListView list;
ListViewAdapter adapter;
SearchView editsearch;
String[] animalNameList;
ArrayList<AnimalNames> arraylist = new ArrayList<AnimalNames>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Resources res = getResources();
String[] animalNameList = res.getStringArray(R.array.animalNameList);
// Locate the ListView in listview_main.xml
list = (ListView) findViewById(R.id.listview);
for (int i = 0; i < animalNameList.length; i++) {
AnimalNames animalNames = new AnimalNames(animalNameList[i]);
// Binds all strings into an array
arraylist.add(animalNames);
}
// Pass results to ListViewAdapter Class
adapter = new ListViewAdapter(this, arraylist);
// Binds the Adapter to the ListView
list.setAdapter(adapter);
// Locate the EditText in listview_main.xml
editsearch = (SearchView) findViewById(R.id.search);
editsearch.setOnQueryTextListener(this);
}
#Override
public boolean onQueryTextSubmit(String newText) {
String text = newText;
adapter.filter(text);
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
return false;
}
===== ListviewAdapter.java:
public class ListViewAdapter extends BaseAdapter {
// Declare Variables
Context mContext;
LayoutInflater inflater;
private List<AnimalNames> animalNamesList = null;
private ArrayList<AnimalNames> arraylist;
public ListViewAdapter(Context context, List<AnimalNames> animalNamesList) {
mContext = context;
this.animalNamesList = animalNamesList;
inflater = LayoutInflater.from(mContext);
this.arraylist = new ArrayList<AnimalNames>();
this.arraylist.addAll(animalNamesList);
}
public class ViewHolder {
TextView name;
}
#Override
public int getCount() {
return animalNamesList.size();
}
#Override
public AnimalNames getItem(int position) {
return animalNamesList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(final int position, View view, ViewGroup parent) {
final ViewHolder holder;
if (view == null) {
holder = new ViewHolder();
view = inflater.inflate(R.layout.listview_item, null);
// Locate the TextViews in listview_item.xml
holder.name = (TextView) view.findViewById(R.id.name);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
// Set the results into TextViews
holder.name.setText(animalNamesList.get(position).getAnimalName());
return view;
}
// Filter Class
public void filter(String charText) {
charText = charText.toLowerCase(Locale.getDefault());
animalNamesList.clear();
if (charText.length() == 0) {
animalNamesList.addAll(arraylist);
} else {
for (AnimalNames wp : arraylist) {
if (wp.getAnimalName().toLowerCase(Locale.getDefault()).contains(charText)) {
animalNamesList.add(wp);
}
}
}
notifyDataSetChanged();
}
////Here My problem starts :
public static SpannableString highlightKeyword(CharSequence text, Pattern p, int fgcolor, int bgcolor) {
SpannableString ss = new SpannableString(text);
ColorStateList blueColor = new ColorStateList(new int[][]{new int[]{}}, new int[]{Color.BLUE});
int start = 0;
int end;
Matcher m = p.matcher(text);
while (m.find(start)) {
start = m.start();
end = m.end();
BackgroundColorSpan bgspan = new BackgroundColorSpan(bgcolor);
ss.setSpan(bgspan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ForegroundColorSpan fgspan = new ForegroundColorSpan(fgcolor);
ss.setSpan(fgspan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
start = end;
}
return ss;
}
////But even did not the result word highlighted.
Thanks in Advance.
Alhamdulillah, At last I found my answer on google. Highligh in custom adapter within getview() method. As follows :
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
TextView text;
if (convertView == null) {
view = mInflater.inflate(R.layout.list_item, null);
} else {
view = convertView;
}
try {
if (mFieldId == 0) {
// If no custom field is assigned, assume the whole resource is a TextView
text = (TextView) view;
} else {
// Otherwise, find the TextView field within the layout
text = (TextView) view.findViewById(mFieldId);
}
} catch (ClassCastException e) {
Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
throw new IllegalStateException(
"ArrayAdapter requires the resource ID to be a TextView", e);
}
// HIGHLIGHT...
String fullText = getItem(position);
if (mSearchText != null && !mSearchText.isEmpty()) {
int startPos = fullText.toLowerCase(Locale.US).indexOf(mSearchText.toLowerCase(Locale.US));
int endPos = startPos + mSearchText.length();
if (startPos != -1) {
Spannable spannable = new SpannableString(fullText);
ColorStateList blueColor = new ColorStateList(new int[][]{new int[]{}}, new int[]{Color.BLUE});
TextAppearanceSpan highlightSpan = new TextAppearanceSpan(null, Typeface.BOLD, -1, blueColor, null);
spannable.setSpan(highlightSpan, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setText(spannable);
} else {
text.setText(fullText);
}
} else {
text.setText(fullText);
}
return view;
}

List adapter doesn't pick up changes after Realm query

I'm using Realm in my mobile application to store line items for my list view. I'm extending the RealmBaseAdapter which is working fine. Problem is that if I make a query to the database to filter my items, my adapter is not picking up the changed list and causing the out of bound index error.
This is where I set my adapter with the initial values,
results = realm.where(BillingLineItem.class).findAll();
adapter = new BillingListAdapter(getActivity(), results);
And this is the part that I'm doing the filtering based on the spec number,
results = realm.where(BillingLineItem.class)
.equalTo("SpecNumber", spec)
.findAll();
adapter.notifyDataSetChanged();
And like I said before After that query results will be updated but adapter doesn't pick up the changes.
EDIT: My adapter for the list view
public class BillingListAdapter extends RealmBaseAdapter<BillingLineItem> {
private LayoutInflater inflater = null;
private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();
private boolean isActionMode;
public BillingListAdapter(Context mContext, RealmResults<BillingLineItem> lineItems) {
super(mContext,lineItems);
inflater = (LayoutInflater) mContext.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.isActionMode = false;
}
// I commented out this part because RealmBaseAdapter automaticly implements this methods in the super class
/*#Override
public int getCount() {
return lineItems.size();
}
#Override
public Object getItem(int position) {
return lineItems.get(position);
}
#Override
public long getItemId(int position) {
return position;
}*/
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
ViewHolder holder;
if (convertView == null) {
/****** Inflate billing_foreground_item.xml file for each row ( Defined below ) *******/
vi = inflater.inflate(R.layout.billing_foreground_item, null);
/****** View Holder Object to contain billing_foreground_item.xml file elements ******/
holder = new ViewHolder();
holder.SubOper = (TextView) vi.findViewById(R.id.tvSubOper);
holder.Spec = (TextView) vi.findViewById(R.id.tvSpec);
holder.Address = (TextView) vi.findViewById(R.id.tvAddress);
holder.SKU = (TextView) vi.findViewById(R.id.tvSku);
holder.SKUDesc = (TextView) vi.findViewById(R.id.tvSkuDesc);
holder.Quantity = (TextView) vi.findViewById(R.id.tvQuantity);
holder.Unit = (TextView) vi.findViewById(R.id.tvUnit);
holder.BilledQty = (TextView) vi.findViewById(R.id.tvBBilledQty);
holder.RemainingQty = (TextView) vi.findViewById(R.id.tvRemainingQty);
holder.ivLineIcon = (ImageView) vi.findViewById(R.id.ivLineIcon);
holder.rlItem = (RelativeLayout) vi.findViewById(R.id.rlItem);
holder.ErrorMessage = (TextView) vi.findViewById(R.id.txtErrorDisplay);
/************ Set holder with LayoutInflater ************/
vi.setTag(holder);
} else {
holder = (ViewHolder) vi.getTag();
}
/************ Set Model values in Holder elements ***********/
if (adapterData.get(position).getFinalFlag()) {
holder.ivLineIcon.setImageResource(R.drawable.finalflagblue);
holder.rlItem.setBackgroundColor(Color.rgb(255, 255, 255));
if (adapterData.get(position).getCompleted()) {
holder.rlItem.setBackgroundColor(Color.rgb(223, 235, 245));
}
if (adapterData.get(position).getErrorFlag()){
holder.rlItem.setBackgroundColor(Color.rgb(231, 25, 57));
holder.ErrorMessage.setVisibility(View.VISIBLE);
holder.ErrorMessage.setText(adapterData.get(position).getErrorMessage());
}
} else if (adapterData.get(position).getDeleteFlag()) {
holder.ivLineIcon.setImageResource(R.drawable.trashiconred);
holder.rlItem.setBackgroundColor(Color.rgb(255, 255, 255));
if (adapterData.get(position).getErrorFlag()){
holder.rlItem.setBackgroundColor(Color.rgb(231, 25, 57));
holder.ErrorMessage.setVisibility(View.VISIBLE);
holder.ErrorMessage.setText(adapterData.get(position).getErrorMessage());
}
} else if (adapterData.get(position).getChanged()) {
holder.ivLineIcon.setImageResource(R.drawable.changedicongreen);
holder.rlItem.setBackgroundColor(Color.rgb(255, 255, 255));
if (adapterData.get(position).getErrorFlag()){
holder.rlItem.setBackgroundColor(Color.rgb(231, 25, 57));
holder.ErrorMessage.setVisibility(View.VISIBLE);
holder.ErrorMessage.setText(adapterData.get(position).getErrorMessage());
}
} else if (adapterData.get(position).getNewLine()) {
holder.ivLineIcon.setImageResource(R.drawable.newlineicon);
holder.rlItem.setBackgroundColor(Color.rgb(255, 255, 255));
if (adapterData.get(position).getErrorFlag()){
holder.rlItem.setBackgroundColor(Color.rgb(231, 25, 57));
holder.ErrorMessage.setVisibility(View.VISIBLE);
holder.ErrorMessage.setText(adapterData.get(position).getErrorMessage());
}
} else {
holder.ivLineIcon.setImageResource(R.drawable.linesiconblack);
holder.rlItem.setBackgroundColor(Color.rgb(255, 255, 255));
holder.ErrorMessage.setVisibility(View.GONE);
}
if (mSelection.get(position) != null) {
//Log.d(TAG, "Item Selected");
holder.rlItem.setBackgroundColor(Color.rgb(255, 255, 192));// this is a selected position so make it hilighted
}
holder.SubOper.setText(adapterData.get(position).getSubOper());
holder.Spec.setText(adapterData.get(position).getSpecNumber());
holder.Address.setText(adapterData.get(position).getAddress());
holder.SKU.setText(adapterData.get(position).getSKUNumber());
holder.SKUDesc.setText(adapterData.get(position).getSKUDesc());
holder.Quantity.setText(adapterData.get(position).getQuantity());
holder.Unit.setText(adapterData.get(position).getUnit());
holder.BilledQty.setText(adapterData.get(position).getBilledQty());
holder.RemainingQty.setText(adapterData.get(position).getRemainingQty());
return vi;
}
public void setNewSelection(int position, boolean value) {
mSelection.put(position, value);
notifyDataSetChanged();
}
public boolean isPositionChecked(int position) {
Boolean result = mSelection.get(position);
return result == null ? false : result;
}
public Set<Integer> getCurrentCheckedPosition() {
return mSelection.keySet();
}
public void removeSelection(int position) {
mSelection.remove(position);
notifyDataSetChanged();
}
public void clearSelection() {
mSelection = new HashMap<Integer, Boolean>();
notifyDataSetChanged();
}
public void setActionMode(boolean isActionMode)
{
this.isActionMode = isActionMode;
}
#Override
public boolean isEnabled(int position)
{
final BillingLineItem item = (BillingLineItem) getItem(position);
if (!item.getDeleteFlag().equals("true"))
{
//only enable items that are not inside the basket
return true;
}
//all other items are disabled during actionmode
return false;
}
public static class ViewHolder {
public TextView SubOper;
public TextView Spec;
public TextView Address;
public TextView SKU;
public TextView SKUDesc;
public TextView Quantity;
public TextView Unit;
public TextView BilledQty;
public TextView RemainingQty;
public ImageView ivLineIcon;
public RelativeLayout rlItem;
public TextView ErrorMessage;
}
}
Some information to point out here:
Android is based on java thus it's variables are passed by reference value (More Info).
It means that if you have an object:
RealmResults<BillingLineItem> results;
And you pass this variable as a parameter to the Adapter constructor:
adapter = new BillingListAdapter(getActivity(), results);
The list which you have outside the adapter and the variable inside the adapter are actually the same objects (two variables pointing to the same reference).
After you make your second query:
results = realm.where(BillingLineItem.class)
.equalTo("SpecNumber", spec)
.findAll();
You are making a new reference and storing it in the result object thus the list outside the adapter and the list which you passed earlier to the adapter are literally different objects so the adapter will not be able to sense the change and you get the error. You can fix it like this:
results.clear();
//adapter.notifyDataSetChanged() if you want to show the change before data fetched...
results.addAll(realm.where(BillingLineItem.class)
.equalTo("SpecNumber", spec)
.findAll());
adapter.notifyDataSetChanged();

I meet a something wrong about IndexOutOfBoundsException

I have a Json data like ,I want to show the information in my listView so I parse them into a ArrayList<String> and then add to the adapter.
{
"areas": [
{
"fieldTag": "1",
"areaId": 2,
"areaName": "No.1",
"devices": [
{
"cameraName": "A",
"busyFields": "null",
"freeFields": "No.1,NO,2",
},{
"cameraName": "B"
"busyFields": "null",
"freeFields": "No.3,No.4",
}
]
}
],
"error": 0,
"message": "ok"
}
and I use Gson to parse it and I make the data to a list for show them in the Adapter,my code is :
if (mDeviceInfo.getError() == 0) {
for (int i = 0; i<mDeviceInfo.getAreas().size();i++){
adapter.addSectionHeaderItem(mDeviceInfo.getAreas().get(i).getAreaName());
for (int k = 0; k<mDeviceInfo.getAreas().get(i).getDevices().size();k++){
adapter.addItem(mDeviceInfo.getAreas().get(i).getDevices().get(k).getCameraName());
mPositionList.add(mDeviceInfo.getAreas().get(i).getDevices().get(k).getPosition());
mFreeFieldsList.add(mDeviceInfo.getAreas().get(i).getDevices().get(k).getFreeFields());
mBusyFieldsList.add(mDeviceInfo.getAreas().get(i).getDevices().get(k).getBusyFields());
}
}
adapter.addMessage(mPosition,mFreeFields,mBusyFields);
}else {
Toast.makeText(this,"get info failed",Toast.LENGTH_SHORT).show();
}
and that is my code as the Adapter:
public class CameraListAdapter extends BaseAdapter {
private static final int TYPE_ITEM = 0;
private static final int TYPE_SEPARATOR = 1;
private ArrayList<String> mData = new ArrayList<String>();
private ArrayList<String> mPosition = new ArrayList<>();
private ArrayList<String> mFreeFields = new ArrayList<>();
private ArrayList<String> mBusyFields = new ArrayList<>();
private TreeSet<Integer> sectionHeader = new TreeSet<Integer>();
private LayoutInflater mInflater;
public CameraListAdapter(Context context) {
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void addItem(final String item) {
mData.add(item);
notifyDataSetChanged();
}
public void addSectionHeaderItem(final String item) {
mData.add(item);
sectionHeader.add(mData.size() - 1);
notifyDataSetChanged();
}
public void addMessage(ArrayList<String> position,ArrayList<String> freeFields,ArrayList<String> busyFields){
mPosition = position;
mFreeFields = freeFields;
mBusyFields = busyFields;
}
#Override
public int getItemViewType(int position) {
return sectionHeader.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getCount() {
return mData.size();
}
#Override
public String getItem(int position) {
return mData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public boolean isEnabled(int position) {
return getItemViewType(position) != TYPE_SEPARATOR;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
int rowType = getItemViewType(position);
if (convertView == null) {
viewHolder = new ViewHolder();
switch (rowType) {
case TYPE_ITEM:
convertView = mInflater.inflate(R.layout.cameradetail, null);
viewHolder.mCameraName = (TextView) convertView.findViewById(R.id.cameraName);
Log.d("CameraListAdapter","position "+position);
viewHolder.mCameraName.setText(mData.get(position));
viewHolder.position = (TextView) convertView.findViewById(R.id.cameraposition);
viewHolder.position.setText(mPosition.get(position));
viewHolder.busyFields = (TextView) convertView.findViewById(R.id.busyfields);
viewHolder.busyFields.setText(mBusyFields.get(position));
viewHolder.freeFields = (TextView) convertView.findViewById(R.id.freefields);
viewHolder.freeFields.setText(mFreeFields.get(position));
break;
case TYPE_SEPARATOR:
convertView = mInflater.inflate(R.layout.cameratag, null);
viewHolder.mTag = (TextView) convertView.findViewById(R.id.areaName);
viewHolder.mTag.setText(mData.get(position));
break;
}
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
return convertView;
}
public static class ViewHolder {
public TextView mTag;
public TextView mCameraName;
public TextView position;
public TextView busyFields;
public TextView freeFields;
}
}
But it always show me that
java.lang.IndexOutOfBoundsException: Invalid index 2, size is 2
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
at java.util.ArrayList.get(ArrayList.java:308)
at .CameraListAdapter.getView(CameraListAdapter.java:106)
It upset me for 2 days so are there anyone to help me ?Thank you so much
In an Adapter, the size of the data is determined by getCount, which you defined like so.
#Override
public int getCount() {
return mData.size();
}
This means that the range of positions in getView is 0 to the value returned by getCount.
But, you seem to have 4 lists of various sizes, or at least not the same size as mData
private ArrayList<String> mData = new ArrayList<String>();
private ArrayList<String> mPosition = new ArrayList<>();
private ArrayList<String> mFreeFields = new ArrayList<>();
private ArrayList<String> mBusyFields = new ArrayList<>();
I'm not sure which one is causing the problem, but, for example, this will throw an exception if mPosition doesn't hold the same amount of data as mData
viewHolder.position.setText(mPosition.get(position));
I would recommend you not have 4 separate String Arraylists since that data seems to pertain to a single Object, which if you want a proper custom Adapter, you give it a list of your Object type (parsed from Gson, for example) instead of individual pieces of an Object
When you use
#Override
public int getCount() {
return mData.size();
}
you basically tell the adapter to call the getView() method for every item in you mData list. The position argument in your getView() method corresponds to the entry in your mData list. You then use that same position argument to get data out of three other lists that are probably of different size than the mData list so one of the get(position) calls on one of the other lists will throw the exception you are seeing. This can be easily debugged if you use the debugger in Android Studio on line 106 in your adapter.

SearchView doesn't work when searching rows in a custom listview

I have a project where I can add data,delete them and update them using SQLite. Of course I display them in a listview. The last thing I want to do is a use a searchView object,so the user can search the data he wants and check if they are there. However the SearchView object does nothing. I type on purpose something that exists in the listview and the matched row doesn't appear.
Here is my code.
public class DisplayForldersActivity extends AppCompatActivity {
DatabaseHandler dba;
private ArrayList<MyFolder> dbFolders = new ArrayList<>();
private FolderAdapter folderAdapter;
private ListView listView;
SearchView inputSearch;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display_forlders);
listView = (ListView)findViewById(R.id.list);
inputSearch = (SearchView) findViewById(R.id.inputSearch);
fetchDataFromDB();
}
private void fetchDataFromDB() {
dbFolders.clear();
dba = new DatabaseHandler(getApplicationContext());
ArrayList<MyFolder> foldersFromDB = dba.getFolderDetails();
for(int i=0; i<foldersFromDB.size();i++){
String plateNo = foldersFromDB.get(i).getPlateNumber();
String owner = foldersFromDB.get(i).getOwnerName();
String cardId = foldersFromDB.get(i).getCardId();
String content = foldersFromDB.get(i).getContent();
String date = foldersFromDB.get(i).getRecordDate();
int mid = foldersFromDB.get(i).getItemId();
MyFolder f = new MyFolder();
f.setPlateNumber(plateNo);
f.setOwnerName(owner);
f.setCardId(cardId);
f.setContent(content);
f.setRecordDate(date);
f.setItemId(mid);
dbFolders.add(f);
folderAdapter = new FolderAdapter(DisplayForldersActivity.this,R.layout.folders_row,dbFolders);
listView.setAdapter(folderAdapter);
folderAdapter.notifyDataSetChanged();
inputSearch.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
folderAdapter.getFilter().filter(newText);
return false;
}
});
}
dba.close();
}
And my adapter's code
private class FolderAdapter extends ArrayAdapter<MyFolder>{
Activity activity;
int layoutResource;
MyFolder myFolder;
ArrayList<MyFolder> mData = new ArrayList<>();
public FolderAdapter(Activity act, int resource, ArrayList<MyFolder> data) {
super(act, resource,data);
activity = act;
layoutResource = resource;
mData = data;
notifyDataSetChanged();
}
#Override
public int getCount() {
return mData.size();
}
#Override
public MyFolder getItem(int position) {
return mData.get(position);
}
#Override
public long getItemId(int position) {
return super.getItemId(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ViewHolder holder = null;
if(row == null || row.getTag()==null){
LayoutInflater inflater = LayoutInflater.from(activity);
row = inflater.inflate(layoutResource,null);
holder = new ViewHolder();
holder.mPlateNo = (TextView)row.findViewById(R.id.plateNumberList);
//holder.mOwner = (TextView)row.findViewById(R.id.ownerName);
holder.mcardId = (TextView)row.findViewById(R.id.idNumber);
//holder.mContent = (TextView)row.findViewById(R.id.processing);
holder.mDate = (TextView)row.findViewById(R.id.dateText);
row.setTag(holder);
}else{
holder = (ViewHolder)row.getTag();
}
holder.myF = getItem(position);
holder.mPlateNo.setText(holder.myF.getPlateNumber());
//holder.mOwner.setText(holder.myF.getOwnerName());
//holder.mcardId.setText(holder.myF.getCardId());
//holder.mPlateNo.setText(holder.myF.getPlateNumber());
holder.mDate.setText(holder.myF.getRecordDate());
final ViewHolder finalHolder = holder;
holder.mPlateNo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String dateText = finalHolder.myF.getRecordDate().toString();
String owner = finalHolder.myF.getOwnerName().toString();
String cardId = finalHolder.myF.getCardId().toString();
String plateNumber = finalHolder.myF.getCardId().toString();
String content = finalHolder.myF.getContent().toString();
int mid = finalHolder.myF.getItemId();
Intent i = new Intent(DisplayForldersActivity.this,DetailedFolderActivity.class);
i.putExtra("id",mid);
i.putExtra("owner",owner);
i.putExtra("cardId",cardId);
i.putExtra("plateNumber",plateNumber);
i.putExtra("content",content);
i.putExtra("dateText",dateText);
startActivity(i);
}
});
return row;
}
class ViewHolder{
MyFolder myF;
int mid;
TextView mPlateNo;
TextView mOwner;
TextView mcardId;
TextView mContent;
TextView mDate;
}
}
Did I do something wrong? Thanks
You need to call notifyDataSetChanged in the adapter attached to the listView to invalidate the ListView and make it draw the changed/removed/inserted lines.
Your array adapter needs to implement Filterable interface, which will override getFilter() method. Then in your getFilter() method you can perform your filtering opertion.
Check out this stackoverflow link, this code might help you out
No results with custom ArrayAdapter Filter
The SerchView doesn't handle any search, it is just a widget. You have to implement the search logic by your own. Try the solution described here:
http://developer.android.com/guide/topics/search/search-dialog.html
You can also do this by calling SearchView.setOnQueryTextListener(SearchView.OnQueryTextListener listener) and pass in a custom implementation of the listener.

How to display the textviews dynamically in a list view in android?

I am working on quiz application. It contains 2 types of tests. The first test contains questions with 3 options fixed. *The second test contains questions with options not fixed. i.e the options may be 4 or 5 or 6 based on the question.*
After the test I need to display the review page. Here is the review page code for the first type of test with fixed options.(Review page should contain the questions displayed for the test at that time)
My Code:
Review.java
public class Review extends Activity {
static ArrayList selectedoptionids = Test1.listarray;
static ArrayList<ArrayList<String>> questionslist = Test2.stringList1;
static ArrayList<ArrayList<String>> alloptionlist = Test2.optionstablelist;
static ArrayList<ArrayList<String>> all = new ArrayList<ArrayList<String>>();
ListView list;
Button next;
String op1, op2, op3, op4, op5;
ArrayList<String> arr1;
ArrayList<String> arr2;
int a, i;
static int k = 0;
static int p = 1;
static List<mainlist> entirelist = new ArrayList<mainlist>();
String quest;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.review);
list = (ListView) findViewById(R.id.listlist);
View header = getLayoutInflater().inflate(
R.layout.listview_header_text, null);
list.addHeaderView(header, null, false);
View footerView = getLayoutInflater().inflate(
R.layout.listview_footer_text, null);
list.addFooterView(footerView, null, false);
next = (Button) findViewById(R.id.next);
eachquestion();
lvAdapter adapter = new lvAdapter(this, entirelist) {
public boolean areAllItemsEnabled() {
return false;
}
public boolean isEnabled(int position) {
return false;
}
};
list.setAdapter(adapter);
}
public void eachquestion() {
arr1 = new ArrayList<String>();
for (i = p - 1; i < p + 3; i++) {
arr1 = questionslist.get(i);
arr2 = new ArrayList<String>();
for (int j = 0; j < 5; j++) {
arr2 = alloptionlist.get(0);
if (j == 0)
op1 = arr2.get(2);
else if (j == 1)
op2 = arr2.get(2);
else if (j == 2)
op3 = arr2.get(2);
}
}
entirelist.add(new mainlist(quest, op1, op2, op3));
}
}
}
mainlist.java
public class mainlist {
String question,option1,option2,option3;
public mainlist(String question,String option1,String option2,String option3) {
super();
this.question = question;
this.option1 = option1;
this.option2= option2;
this.option3 = option3;
}
public String getquestion() {
return question;
}
public void setquestion(String question) {
this.question = question;
}
public String getoption1() {
return option1;
}
public void setoption1(String option1) {
this.option1 = option1;
}
public String getoption2() {
return option2;
}
public void setoption2(String option2) {
this.option2 = option2;
}
public String getoption3() {
return option3;
}
public void setoption3(String option3) {
this.option3 = option3;
}
}
lvAdapter.java
public class lvAdapter extends BaseAdapter implements OnClickListener {
private Context context;
List<mainlist> list11 = Review.entirelist;
public lvAdapter(Context context, List<mainlist> list11 ) {
this.context = context;
this.list11 = list11 ;
}
public int getCount() {
return list11.size();
}
public Object getItem(int position) {
return list11.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup viewGroup) {
mainlist inst = list11.get(position);
if (convertView == null)
{
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.reviewrow, null);
}
TextView question = (TextView) convertView.findViewById(R.id.textView2);
tvPhone.setText(inst.getquestion());
TextView option1 = (TextView) convertView.findViewById(R.id.op1);
option1.setText(inst.getoption1());
TextView option2 = (TextView) convertView.findViewById(R.id.op2);
option2.setText(inst.getoption2());
TextView option3 = (TextView) convertView.findViewById(R.id.op3);
option3.setText(inst.getoption3());
return convertView;
}
#Override
public void onClick(View v) {
}
}
Now my problem is for the first test as we know the number of options are 3 so I have written the setter and getter methods for 1 question and 3 options and it is working fine. But for the second type of test how can I write the mainlist.java class for generating the setter and getter methods for unknown number of options.
Please help me regarding this...I am struggling for this since 3 days....
Thanks in advance...
Ok what you will need is to make a class named Quiz something like this:
public class Quiz
{
private String question;
private ArrayList<String> options; // no need to get separate variable for every option
public Quiz(String q, ArrayList<String> o)
{
super(); this.question = q; this.options = o;
}
//setter and getter.. like setOptions() getOptions() setQuestion() etc.. :/
}
now in make your entirelist a Quiz List like this:
List<Quiz> entirelist = new ArrayList<Quiz>();
and now pass single quiz + options to entirelist like this:
ArrayList<String> options = new ArrayList<String>();
options.add(op1);
options.add(op2);
options.add(op3);
entirelist.add(new Quiz(quest,options));
now in adapter's getView(), remove all options' TextViews and add them dnamically something like this:
public View getView(int position, View convertView, ViewGroup viewGroup) {
if (convertView == null)
{
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.reviewrow, null);
}
Quiz currentQuiz = list11.get(position);
TextView question = (TextView) convertView.findViewById(R.id.textView2);
tvPhone.setText(currentQuiz.getquestion());
// now add options dynamically..
ArrayList<String> options = currentQuiz.getOptions();
for(String option : options)
{
TextView optionTextView = new TextView(context);
optionTextView.setText(option);
convertView.add(optionTextView);
}
return convertView;
}
Don't use XML.
Add the Views to the layout programatically, in Java. Then you can pick up the appropriate number of options dynamically.
(Almost) Everything in Android that you can do in XML, you can do in Java code.

Categories

Resources