How do i get The answers selected in testFragment which is set in the customlistadapter? - custom-lists

This is my testfragment where i build the list based on a switch:
public class TestFragment extends Fragment {
public String Workshop;
public int NumberOfQuestions;
public Button SaveButton;
public TextView GradeView;
public String Grades;
private OnFragmentInteractionListener mListener;
public TestFragment() {}
#Override
public View onCreateView (LayoutInflater inflater, ViewGroup container,
Bundle bundle){
View rootView = inflater.inflate(R.layout.questionscomplete,
container, false);
Workshop = getArguments().getString("Workshoptitle");
GradeView = ((TextView) rootView.findViewById(R.id.GradeView));
// Inflate the layout for this fragment
if (!Workshop.equals("Doodlz Drawing")) {
rootView = inflater.inflate(R.layout.fragment_test,
container, false);
final ListView lv1 = (ListView) rootView.findViewById(R.id.listview);
ArrayList image_details = getListData();
lv1.setAdapter(new CustomListAdapter(getActivity(), image_details));
SaveButton = ((Button) rootView.findViewById(R.id.Save));
SaveButton.setOnClickListener(Grading);
}
return rootView;
}
private ArrayList getListData() {
ArrayList<QuestionItem> results = new ArrayList<QuestionItem>();
switch (Workshop) {
case "Android Studio":
Workshop = "Android Studio";
QuestionItem[] Item1 = new QuestionItem[3];
QItem WS1 = new QItem();
NumberOfQuestions = Item1.length;
String[] WS1Q = WS1.getQuestions(Workshop, NumberOfQuestions);
String[] WS1QA = WS1.getAnswers(Workshop);
for (int i = 0; i < NumberOfQuestions; i++) {
Item1[i] = new QuestionItem();
Item1[i].setQuestion(WS1Q[i]);
Item1[i].setAnswer1(WS1QA[i * 10 + 0]);
Item1[i].setAnswer2(WS1QA[i * 10 + 1]);
Item1[i].setAnswer3(WS1QA[i * 10 + 2]);
Item1[i].setAnswer4(WS1QA[i * 10 + 3]);
// Toast.makeText(getActivity(), Item1[i].GetAnswer(),
// Toast.LENGTH_LONG).show();
results.add(Item1[i]);
}
break;
case "Java Basics":
Workshop = "Java Basics";
QuestionItem[] Item2 = new QuestionItem[5];
QItem WS2 = new QItem();
NumberOfQuestions = Item2.length;
String[] WS2Q = WS2.getQuestions(Workshop, NumberOfQuestions);
String[] WS2QA = WS2.getAnswers(Workshop);
for (int i = 0; i < NumberOfQuestions; i++) {
Item2[i] = new QuestionItem();
Item2[i].setQuestion(WS2Q[i]);
Item2[i].setAnswer1(WS2QA[i * 10 + 0]);
Item2[i].setAnswer2(WS2QA[i * 10 + 1]);
Item2[i].setAnswer3(WS2QA[i * 10 + 2]);
Item2[i].setAnswer4(WS2QA[i * 10 + 3]);
results.add(Item2[i]);
// Toast.makeText(getActivity(), Item2[i].GetAnswer(),
// Toast.LENGTH_LONG).show();
}
break;
My listadapter:
public class CustomListAdapter extends BaseAdapter {
private LayoutInflater layoutInflater;
public String Answer;
public String[] Answers;
private ArrayList<QuestionItem> listData;
public CustomListAdapter(Context aContext, ArrayList<QuestionItem> listData) {
this.listData = listData;
layoutInflater = LayoutInflater.from(aContext);
}
#Override
public int getCount() {
return listData.size();
}
#Override
public Object getItem(int position) {
return listData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
View v = convertView;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.questions, null);
holder = new ViewHolder(
);
holder.QuestionView = (TextView) convertView.findViewById(R.id.q);
holder.Answer1View = (TextView) convertView.findViewById(R.id.qa1);
holder.Answer2View = (TextView) convertView.findViewById(R.id.qa2);
holder.Answer3View = (TextView) convertView.findViewById(R.id.qa3);
holder.Answer4View = (TextView) convertView.findViewById(R.id.qa4);
//
holder.Answer1Button = (RadioButton) convertView.findViewById(R.id.qa1);
holder.Answer2Button = (RadioButton) convertView.findViewById(R.id.qa2);
holder.Answer3Button = (RadioButton) convertView.findViewById(R.id.qa3);
holder.Answer4Button = (RadioButton) convertView.findViewById(R.id.qa4);
holder.ButtonGroupView = (RadioGroup) convertView.findViewById(R.id.group);
convertView.setTag(holder);
holder.ButtonGroupView.getCheckedRadioButtonId();
holder.ButtonGroupView
.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
public void onCheckedChanged(RadioGroup group,
int checkedId) {
Integer pos = (Integer) group.getTag(); // To identify the Model object i get from the RadioGroup with getTag()
// an integer representing the actual position
QuestionItem element = listData.get(pos);
switch (checkedId) { //set the Model to hold the answer the user picked
case R.id.qa1:
element.setAnswer("1");
"Log.d(listData.get(pos).toString(), element.GetAnswer());
break;
case R.id.qa2:
element.setAnswer("2");
"Log.d(listData.get(pos).toString(), element.GetAnswer());
break;
case R.id.qa3:
element.setAnswer("3");
"Log.d(listData.get(pos).toString(), element.GetAnswer());
break;
case R.id.qa4:
element.setAnswer("4");
"Log.d(listData.get(pos).toString(), element.GetAnswer());
break;
default:
element.setAnswer(null); // Something was wrong set to the default
}
}
});
} else {
holder = (ViewHolder) convertView.getTag();
}
QuestionItem element = (QuestionItem) getItem(position);
holder.QuestionView.setText(listData.get(position).getQuestion());
holder.Answer1View.setText(listData.get(position).Getanswer1());
holder.Answer2View.setText(listData.get(position).Getanswer2());
holder.Answer3View.setText(listData.get(position).Getanswer3());
holder.Answer4View.setText(listData.get(position).Getanswer4());
holder.ButtonGroupView.setTag(new Integer(position)); // I passed the current position as a tag
return convertView;
}
//
class ViewHolder {
TextView t = null;
TextView QuestionView;
TextView Answer1View;
TextView Answer2View;
TextView Answer3View;
TextView Answer4View;
RadioButton Answer1Button, Answer2Button, Answer3Button, Answer4Button;
RadioGroup ButtonGroupView;
}}
My question Item:
public class QuestionItem {
private String Question;
private String Answer1;
private String Answer2;
private String Answer3;
private String Answer4;
private String Answer;
public String getQuestion() {
return Question;
}
public void setQuestion(String Question) {
this.Question = Question;
}
public String Getanswer1() {
return Answer1;
}
public void setAnswer1(String Answer1) {
this.Answer1 = Answer1;
}
public String Getanswer2() {
return Answer2;
}
public void setAnswer2(String Answer2) {
this.Answer2 = Answer2;
}
public String Getanswer3() {
return Answer3;
}
public void setAnswer3(String Answer3) {
this.Answer3 = Answer3;
}
public String Getanswer4() {
return Answer4;
}
public void setAnswer4(String Answer4) {
this.Answer4 = Answer4;
}
public String GetAnswer() {
return Answer;
}
public void setAnswer(String Answer) {
this.Answer = Answer;
}
}
I want to save all the answers and compare them to the right answers and save the score to be used as a variable in another fragment.
UPDATE: added "Log.d(listData.get(pos).toString(), element.GetAnswer());" And i can see it is storing the choses answers so how do i request them in the testfragment and check them
Ive tried several things but cannot find the solution. pls help:)?

Related

Android, ListView adapter

I have some question regarding Android programming. More specific, I have a ListView where every single row is containg five widgets and each trigger event. I have created custom adapter and defined events handler for every widgets in the getView method. Everything works fine, however the code looks quite long, unreadable and nasty because of all these event handlers inside. Is there any better design? Maybe Creating event handlers outside the getView method or something else?
greetings
According to suggestion I posted part of the source code. As you can see I have created few event handlers outside the getView method and two inside. I really do not know which design is better.
public class ListViewAdapter extends ArrayAdapter<HourReport> {
private static Activity context;
private int resourcecId;
private TextView fromTime;
private TextView toTime;
private TextView total;
private HourReport rowModelBean;
private HourReport rowBean;
private CheckBox billable;
private ArrayList<HourReport> list;
private HourReportCatalog catalog;
private Map<Integer, Integer>selectedItems;
private Map<Integer, Integer>selectedRoles;
public ListViewAdapter(Activity context, int resourcecId,
ArrayList<HourReport> list, HourReportCatalog catalog) {
super(context, resourcecId, list);
this.catalog = catalog;
ListViewAdapter.context = context;
this.resourcecId = resourcecId;
this.list = list;
selectedItems = new HashMap<Integer, Integer>();
selectedRoles = new HashMap<Integer, Integer>();
}
// event handler for delete button "-"
private OnClickListener delete = new OnClickListener() {
#Override
public void onClick(View deletBtnF) {
int myPosition = (Integer) deletBtnF.getTag();
HourReport r = list.remove(myPosition);
selectedItems.put(myPosition, 0);
selectedRoles.put(myPosition, 0);
r.setTimeFinished(null);
r.setTimeStarted(null);
r.setTaks(null);
r.setTotal(0.0);
r.setBillable(false);
r.setEngagementContractID(0);
list.add(myPosition, r);
notifyDataSetChanged();
if (r.getDateCreated() != null) {
Log.e("Listview adapter", "inside the if statement");
Long id = r.getHourReportID();
Log.e("", "date created" + r.getDateCreated());
catalog.deleteHourReport(r);
r.setDateCreated(null);
}
}
};
// event handler for textView which is responsible for defining dateFrom
Calendar c = Calendar.getInstance();
OnClickListener onClickLisOnDateFrom = new OnClickListener() {
#Override
public void onClick(View editField) {
Integer position1 = (Integer) editField.getTag();
TableRow parent = (TableRow) editField.getParent();
fromTime = (TextView) parent.findViewById(R.id.viewTextFrom);
total = (TextView) parent.findViewById(R.id.textViewShowsTotal);
rowBean = getModel(position1);
TimePickerDialog.OnTimeSetListener timeListener1 = new TimePickerDialog.OnTimeSetListener() {
#Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
int hour = c.get(Calendar.HOUR_OF_DAY);
int minutes = c.get(Calendar.MINUTE);
String time = hour + ":" + minutes;
fromTime.setText(time);
setTimeFieldFrom(time);
String totalTime = totalHourCalculator();
total.setText(totalTime);
}
};
new TimePickerDialog(context, timeListener1,
c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), true)
.show();
}
};
// event handler for textView which is responsible for defining dateTo
private OnClickListener onClickLisOnDateTo = new OnClickListener() {
#Override
public void onClick(View editField) {
Integer position1 = (Integer) editField.getTag();
Log.e("ListView - Timer ", "position: " + position1);
TableRow parent = (TableRow) editField.getParent();
toTime = (TextView) parent.findViewById(R.id.viewTextFrom);
total = (TextView) parent.findViewById(R.id.textViewShowsTotal);
rowBean = getModel(position1);
TimePickerDialog.OnTimeSetListener timeListener2 = new TimePickerDialog.OnTimeSetListener() {
#Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
int hour = c.get(Calendar.HOUR_OF_DAY);
int minutes = c.get(Calendar.MINUTE);
String time = hour + ":" + minutes;
toTime.setText(time);
setTimeFieldTo(time);
String totalTime = totalHourCalculator();
total.setText(totalTime);
}
};
new TimePickerDialog(context, timeListener2,
c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), true)
.show();
}
};
// event handler for check box
private OnClickListener checkBoxListener = new OnClickListener() {
#Override
public void onClick(View checkBox) {
Integer num = (Integer) checkBox.getTag();
rowBean = getModel(num);
if (rowBean.isBillable()) {
rowBean.setBillable(false);
} else {
rowBean.setBillable(true);
}
}
};
#Override
public View getView( int position, View convertView, ViewGroup parent) {
getHourReportList();
TextView deleteBtnV = null;
View row = convertView;
Spinner taskSpinner, roleSpinner;
TextView addReport;
final ViewHolder viewHolder;
if (row == null) {
LayoutInflater layoutInflater = context.getLayoutInflater();
row = layoutInflater.inflate(resourcecId, parent, false);
viewHolder = new ViewHolder(row);
fromTime = viewHolder.getFromTime();
deleteBtnV = viewHolder.getDeleteBtnVView();
deleteBtnV.setOnClickListener(delete);
billable = viewHolder.getCheckBox();
addReport = viewHolder.getAddButtonView();
// event handler for the button "+" which adds extra row
addReport.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
Integer myPosition = (Integer) view.getTag();
HourReport report = list.get(myPosition);
HourReport nReport = new HourReport();
nReport.setClaimDate(report.getClaimDate());
nReport.setEmployeeID(report.getEmployeeID());
nReport.setBillable(false);
nReport.setEngagementContractID(0);
list.add(myPosition + 1, nReport);
notifyDataSetChanged();
}
});
viewHolder.adapter = new SpinerAdapter(context);
taskSpinner = viewHolder.getSpinnerTask();
roleSpinner = viewHolder.getSpinnerRole();
//event handler for the spinner
taskSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View spin,
int selected, long arg3) {
Spinner spinner = (Spinner) spin.getParent();
Integer myPosition = (Integer) spinner.getTag();
viewHolder.adapter.setSelected(selected);
String task = viewHolder.adapter.getSelectcetdTask();
long engmId = viewHolder.adapter.getSelectedTaskID();
rowBean = getModel(myPosition);
rowBean.setTaks(task);
rowBean.setEngagementContractID(engmId);
selectedItems.put(myPosition, selected);
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
////event handler for the spinner
roleSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View spin,
int selectedRole, long arg3) {
Spinner spinner = (Spinner) spin.getParent();
Integer myPosition = (Integer) spinner.getTag();
selectedRoles.put(myPosition, selectedRole);
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
});
fromTime = viewHolder.getFromTime();
toTime = viewHolder.getToTime();
fromTime.setOnClickListener(onClickLisOnDateFrom);
toTime.setOnClickListener(onClickLisOnDateTo);
billable.setOnClickListener(checkBoxListener);
row.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) row.getTag();
fromTime = viewHolder.getFromTime();
toTime = viewHolder.getToTime();
taskSpinner = viewHolder.getSpinnerTask();
roleSpinner = viewHolder.getSpinnerRole();
total = viewHolder.getTotal();
billable = viewHolder.getCheckBox();
TextView date = viewHolder.getDate();
deleteBtnV = viewHolder.getDeleteBtnVView();
addReport = viewHolder.getAddButtonView();
}
HourReport model = getModel(position);
Integer selection = 0;
if (selectedItems.get(position) != null) {
selection = selectedItems.get(position);
}
int selectionR = 0;
if (selectedRoles.get(position) != null) {
selectionR = selectedRoles.get(position);
}
viewHolder.getFromTime().setText(
parseDateToString(model.getTimeStarted()));
viewHolder.getToTime().setText(
parseDateToString(model.getTimeFinished()));
viewHolder.getTotal().setText(
convertDoubleTotToStringTot(model.getTotal()));
viewHolder.getDate().setText(
parseDateToStringDDate(model.getClaimDate()));
viewHolder.getCheckBox().setChecked(model.isBillable());
Log.e("", "tag " + selection + " date " + model.getClaimDate());
viewHolder.taskSpinner.setSelection(selection);
viewHolder.roleSpinner.setSelection(selectionR);
fromTime.setTag(Integer.valueOf(position));
toTime.setTag(Integer.valueOf(position));
taskSpinner.setTag(Integer.valueOf(position));
roleSpinner.setTag(Integer.valueOf(position));
billable.setTag(Integer.valueOf(position));
deleteBtnV.setTag(Integer.valueOf(position));
addReport.setTag(Integer.valueOf(position));
return row;
}![here you have screen shoot of single row][1]
Create a single instance of OnClickListener (for example as an inner class) and assign this instance to every widget. If you need to know which row this widget belongs to, you can call setTag("pos", position) on that widget. By doing this you will be able to get position by calling view.getTag("pos") in onClick(View view) method of the listener. Hope this helps.

Passing Text From Listview to another Activity when I click action item of the listview

I have a listview in my main activity.In each row i have a image view,when i click that image view QuickAction(Like popover in ios) will appears.My request is, I want to setText the text from listview to the another Activity's edittext when i click the action item in the quick action.Please help..
Here is my Main Activity
public class ExampleActivity extends Activity {
private static final int ID_UP = 1;
private static final int ID_DOWN = 2;
private static final int ID_SEARCH = 3;
private static final int ID_INFO = 4;
private QuickAction quickAction;
private ActionItem nextItem;
private ActionItem prevItem;
private ActionItem searchItem;
private ListView view;
private ContactsAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
view = (ListView) findViewById(R.id.listView1);
final Weather weather_data[] = new Weather[] { new Weather(R.drawable.icon, "Cloudy"),
new Weather(R.drawable.icon, "Showers"),
new Weather(R.drawable.icon, "Snow"),
new Weather(R.drawable.icon, "Storm"),
new Weather(R.drawable.icon, "Sunny")
};
adapter = new ContactsAdapter(this, R.layout.main1,
weather_data);
}
void functiontorun(View view1) {
quickAction = new QuickAction(this,QuickAction.HORIZONTAL);
nextItem = new ActionItem(ID_DOWN, "Next", getResources().getDrawable(
R.drawable.menu_down_arrow));
prevItem = new ActionItem(ID_UP, "Prev", getResources().getDrawable(
R.drawable.menu_up_arrow));
searchItem = new ActionItem(ID_SEARCH, "Find", getResources()
.getDrawable(R.drawable.menu_search));
// use setSticky(true) to disable QuickAction dialog being dismissed
// after an item is clicked
prevItem.setSticky(true);
nextItem.setSticky(true);
// add action items into QuickAction
quickAction.addActionItem(nextItem);
quickAction.addActionItem(prevItem);
quickAction.addActionItem(searchItem);
// Set listener for action item clicked
final int position1 = view.getPositionForView(view1);
quickAction
.setOnActionItemClickListener(new QuickAction.OnActionItemClickListener() {
#Override
public void onItemClick(QuickAction source, int pos,
int actionId) {
ActionItem actionItem = quickAction.getActionItem(pos);
if (actionId == ID_SEARCH) {
Intent i=new Intent(ExampleActivity.this,Second.class);
i.putExtra("position",position1 );
Log.v("position","po" +position1);
startActivity(i);
} else if (actionId == ID_INFO) {
Toast.makeText(getApplicationContext(),
"I have no info this time",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(),
actionItem.getTitle() + " selected",
Toast.LENGTH_SHORT).show();
}
}
});
quickAction.show(view1);
}
}
And my another activity
public class Second extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
EditText d1=(EditText) findViewById(R.id.editText1);
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
final int position = (Integer) bundle.get("position");
Log.v("position","po1 " +position);
ArrayList<Weather> array = new ArrayList<Weather>();
Log.v("position","po1 " +array);
}
}
}
contacts adapter:
public class ContactsAdapter extends ArrayAdapter{
Context context;
int layoutResourceId;
Weather data[] = null;
public ContactsAdapter(Context context, int layoutResourceId, Weather[] data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
WeatherHolder holder = null;
if(row == null)
{
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new WeatherHolder();
holder.imgIcon = (ImageView)row.findViewById(R.id.imageView1);
holder.imgIcon.findViewById(R.id.imageView1).setOnClickListener(mBuyButtonClickListener);
holder.txtTitle = (TextView)row.findViewById(R.id.textView1);
//((ImageView) row.findViewById(R.id.im)).setOnClickListener(mBuyButtonClickListener);
row.setTag(holder);
}
else
{
holder = (WeatherHolder)row.getTag();
}
Weather weather = data[position];
holder.txtTitle.setText(weather.title);
holder.imgIcon.setImageResource(weather.icon);
return row;
}
static class WeatherHolder
{
ImageView imgIcon;
TextView txtTitle;
}
private OnClickListener mBuyButtonClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
((ExampleActivity) context).functiontorun(v);
}
};
}
private OnClickListener mBuyButtonClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
WeatherHolder holder = (WeatherHolder) v.getTag;
String string = holder.txtTitle .getText();
((ExampleActivity) context).functiontorun(string);
}
};
just pass the string to your activity and change the parameter of the functiontorun as string

Autocomplete textbox highlighting the typed character in the suggestion list

I have been working on AutoCompleteTextView. I was able to get the suggestion and all in the drop down list as we type.
My question is: Can we highlight the typed character in the suggestion drop down list?
I have achieved the functionality. The solution is as follows:
AutoCompleteAdapter.java
public class AutoCompleteAdapter extends ArrayAdapter<String> implements
Filterable {
private ArrayList<String> fullList;
private ArrayList<String> mOriginalValues;
private ArrayFilter mFilter;
LayoutInflater inflater;
String text = "";
public AutoCompleteAdapter(Context context, int resource,
int textViewResourceId, List<String> objects) {
super(context, resource, textViewResourceId, objects);
fullList = (ArrayList<String>) objects;
mOriginalValues = new ArrayList<String>(fullList);
inflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return fullList.size();
}
#Override
public String getItem(int position) {
return fullList.get(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
// tvViewResourceId = (TextView) view.findViewById(android.R.id.text1);
String item = getItem(position);
Log.d("item", "" + item);
if (convertView == null) {
convertView = view = inflater.inflate(
android.R.layout.simple_dropdown_item_1line, null);
}
// Lookup view for data population
TextView myTv = (TextView) convertView.findViewById(android.R.id.text1);
myTv.setText(highlight(text, item));
return view;
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
return mFilter;
}
private class ArrayFilter extends Filter {
private Object lock;
#Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (prefix != null) {
text = prefix.toString();
}
if (mOriginalValues == null) {
synchronized (lock) {
mOriginalValues = new ArrayList<String>(fullList);
}
}
if (prefix == null || prefix.length() == 0) {
synchronized (lock) {
ArrayList<String> list = new ArrayList<String>(
mOriginalValues);
results.values = list;
results.count = list.size();
}
} else {
final String prefixString = prefix.toString().toLowerCase();
ArrayList<String> values = mOriginalValues;
int count = values.size();
ArrayList<String> newValues = new ArrayList<String>(count);
for (int i = 0; i < count; i++) {
String item = values.get(i);
if (item.toLowerCase().contains(prefixString)) {
newValues.add(item);
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
if (results.values != null) {
fullList = (ArrayList<String>) results.values;
} else {
fullList = new ArrayList<String>();
}
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
public static CharSequence highlight(String search, String originalText) {
// ignore case and accents
// the same thing should have been done for the search text
String normalizedText = Normalizer
.normalize(originalText, Normalizer.Form.NFD)
.replaceAll("\\p{InCombiningDiacriticalMarks}+", "")
.toLowerCase(Locale.ENGLISH);
int start = normalizedText.indexOf(search.toLowerCase(Locale.ENGLISH));
if (start < 0) {
// not found, nothing to to
return originalText;
} else {
// highlight each appearance in the original text
// while searching in normalized text
Spannable highlighted = new SpannableString(originalText);
while (start >= 0) {
int spanStart = Math.min(start, originalText.length());
int spanEnd = Math.min(start + search.length(),
originalText.length());
highlighted.setSpan(new ForegroundColorSpan(Color.BLUE),
spanStart, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
start = normalizedText.indexOf(search, spanEnd);
}
return highlighted;
}
}
}
MainActivity.java
public class MainActivity extends Activity {
String[] languages = { "C", "C++", "Java", "C#", "PHP", "JavaScript",
"jQuery", "AJAX", "JSON" };
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
List<String> wordList = new ArrayList<String>();
Collections.addAll(wordList, languages);
AutoCompleteAdapter adapter = new AutoCompleteAdapter(this,
android.R.layout.simple_dropdown_item_1line,
android.R.id.text1,wordList);
AutoCompleteTextView acTextView = (AutoCompleteTextView) findViewById(R.id.languages);
acTextView.setThreshold(1);
acTextView.setAdapter(adapter);
}
}
Working like charm!
Enjoy!
I reckon that should be possible, provided you know the index/indices of the character(s) the user typed last. You can then use a SpannableStringBuilder and set a ForegroundColorSpan and BackgroundColorSpan to give the character(s) the appearance of a highlight.
The idea looks somewhat like this:
// start & end of the highlight
int start = ...;
int end = ...;
SpannableStringBuilder builder = new SpannableStringBuilder(suggestionText);
// set foreground color (text color) - optional, you may not want to change the text color too
builder.setSpan(new ForegroundColorSpan(Color.RED), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// set background color
builder.setSpan(new BackgroundColorSpan(Color.YELLOW), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// set result to AutoCompleteTextView
autocompleteTextview.setText(builder);
Note that the 'highlight' will remain as long as you don't type another character. You may want to remove the highlight when e.g. the user changes the cursor position in the AutoCompleteTextView, but I'll leave that up to you.
I know it's to late for answering this question , But as I personally battled to find the answer , finally I wrote it myself (with the help of the answer from #MH. ofcourse), so here it is :
First , You have to create a Custom ArrayAdapter :
public class AdapterAustocomplete extends ArrayAdapter<String> {
private static final String TAG = "AdapterAustocomplete";
String q = "";
public AdapterAustocomplete(Context context, int resource, List objects) {
super(context, resource, objects);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
String item = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView =
// I'll use a custom view for each Item , this way I can customize it also!
G.inflater.from(getContext()).inflate(R.layout.textview_autocomplete, parent, false);
}
// Lookup view for data population
TextView myTv = (TextView) convertView.findViewById(R.id.txt_autocomplete);
int start = item.indexOf(q);
int end = q.length()+start;
SpannableStringBuilder builder = new SpannableStringBuilder(item);
builder.setSpan(new ForegroundColorSpan(Color.RED), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
myTv.setText(builder);
return convertView;
}
public void setQ(String q) {
this.q = q;
}
}
And in the Code that you want to set the adapter for AutoCompleteTextView ;
AutoCompleteTextView myAutoComplete = findViewById(its_id);
AdapterAustocomplete adapter_autoComplete = new AdapterAustocomplete(getActivity(), 0, items); // items is an arrayList of Strings
adapter_autoComplete.setQ(q);
myAutoComplete.setAdapter(adapter_autoComplete);
Thanks to vadher jitendra I wrote the same and fixed some bugs.
Changed a dropdown layout to own.
Added showing a full list when clicking inside AutoCompleteTextView.
Fixed a bug of freezing the list when showing (added a check for empty string in highlight).
row_dropdown.xml (item layout):
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/text1"
style="?android:attr/dropDownItemStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:ellipsize="marquee"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:singleLine="true"
android:textColor="#333333"
android:textSize="15sp"
tools:text="text"
tools:textAppearance="?android:attr/textAppearanceLargePopupMenu" />
To filter a list when typing we should implement ArrayAdapter. It depends on items (T class). You can later use AutoCompleteAdapter<String> or any data class you like.
AutoCompleteAdapter:
import android.content.Context;
import android.graphics.Typeface;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.StyleSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class AutoCompleteAdapter<T> extends ArrayAdapter<T> implements Filterable {
private Context context;
#LayoutRes
private int layoutRes;
#IdRes
private int textViewResId;
private ArrayList<T> fullList;
private ArrayList<T> originalValues;
private ArrayFilter filter;
private LayoutInflater inflater;
private String query = "";
public AutoCompleteAdapter(#NonNull Context context, #LayoutRes int resource, #IdRes int textViewResourceId, #NonNull List<T> objects) {
super(context, resource, textViewResourceId, objects);
this.context = context;
layoutRes = resource;
textViewResId = textViewResourceId;
fullList = (ArrayList<T>) objects;
originalValues = new ArrayList<>(fullList);
inflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return fullList.size();
}
#Override
public T getItem(int position) {
return fullList.get(position);
}
/**
* You can use either
* vadher jitendra method (getView)
* or get the method from ArrayAdapter.java.
*/
// #NotNull
// #Override
// public View getView(int position, View convertView, ViewGroup parent) {
// View view = convertView;
// T item = getItem(position);
// Log.d("item", "" + item);
// if (convertView == null) {
// convertView = view = inflater.inflate(layoutRes, null);
// }
// // Lookup view for data population
// TextView myTv = convertView.findViewById(textViewResId);
// myTv.setText(highlight(query, item));
// return view;
// }
#Override
public #NonNull
View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
return createViewFromResource(inflater, position, convertView, parent, layoutRes);
}
private #NonNull
View createViewFromResource(#NonNull LayoutInflater inflater, int position,
#Nullable View convertView, #NonNull ViewGroup parent, int resource) {
final View view;
final TextView text;
if (convertView == null) {
view = inflater.inflate(resource, parent, false);
} else {
view = convertView;
}
try {
if (textViewResId == 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 = view.findViewById(textViewResId);
if (text == null) {
throw new RuntimeException("Failed to find view with ID "
+ context.getResources().getResourceName(textViewResId)
+ " in item layout");
}
}
} 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);
}
final T item = getItem(position);
text.setText(highlight(query, item.toString()));
// if (item instanceof CharSequence) {
// text.setText(highlight(query, (CharSequence) item));
// } else {
// text.setText(item.toString());
// }
return view;
}
#Override
public #NonNull
Filter getFilter() {
if (filter == null) {
filter = new ArrayFilter();
}
return filter;
}
private class ArrayFilter extends Filter {
private final Object lock = new Object();
#Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (prefix == null) {
query = "";
} else {
query = prefix.toString();
}
if (originalValues == null) {
synchronized (lock) {
originalValues = new ArrayList<>(fullList);
}
}
if (prefix == null || prefix.length() == 0) {
synchronized (lock) {
ArrayList<T> list = new ArrayList<>(originalValues);
results.values = list;
results.count = list.size();
}
} else {
final String prefixString = prefix.toString().toLowerCase();
ArrayList<T> values = originalValues;
int count = values.size();
ArrayList<T> newValues = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
T item = values.get(i);
if (item.toString().toLowerCase().contains(prefixString)) {
newValues.add(item);
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.values != null) {
fullList = (ArrayList<T>) results.values;
} else {
fullList = new ArrayList<>();
}
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
private static CharSequence highlight(#NonNull String search, #NonNull CharSequence originalText) {
if (search.isEmpty())
return originalText;
// ignore case and accents
// the same thing should have been done for the search text
String normalizedText = Normalizer
.normalize(originalText, Normalizer.Form.NFD)
.replaceAll("\\p{InCombiningDiacriticalMarks}+", "")
.toLowerCase(Locale.ENGLISH);
int start = normalizedText.indexOf(search.toLowerCase(Locale.ENGLISH));
if (start < 0) {
// not found, nothing to do
return originalText;
} else {
// highlight each appearance in the original text
// while searching in normalized text
Spannable highlighted = new SpannableString(originalText);
while (start >= 0) {
int spanStart = Math.min(start, originalText.length());
int spanEnd = Math.min(start + search.length(),
originalText.length());
highlighted.setSpan(new StyleSpan(Typeface.BOLD), spanStart, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
start = normalizedText.indexOf(search, spanEnd);
}
return highlighted;
}
}
}
In order to show dropdown list when clicked inside AutoCompleteTextView we need to override setOnTouchListener as described in https://stackoverflow.com/a/26036902/2914140. Lint also prints warnings, so we have to write a custom view:
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.appcompat.widget.AppCompatAutoCompleteTextView;
/*
Avoids a warning "Custom view `AutoCompleteTextView` has setOnTouchListener called on it but does not override performClick".
*/
public class AutoCompleteTV extends AppCompatAutoCompleteTextView {
public AutoCompleteTV(Context context) {
super(context);
}
public AutoCompleteTV(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AutoCompleteTV(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
performClick();
}
return super.onTouchEvent(event);
}
#Override
public boolean performClick() {
super.performClick();
return true;
}
}
Then use it in activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.example.autocompletetextview1.AutoCompleteTV
android:id="#+id/languages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:completionThreshold="1"
android:hint="language"
android:imeOptions="actionNext"
android:maxLines="1"
android:paddingLeft="10dp"
android:paddingTop="15dp"
android:paddingRight="10dp"
android:paddingBottom="15dp"
android:singleLine="true"
android:textColor="#333333"
android:textColorHint="#808080"
android:textSize="12sp" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
I use TextInputLayout here for better decoration, in this case we have to add Material Design Components:
in build.gradle:
implementation 'com.google.android.material:material:1.3.0-alpha01'
and in styles.xml:
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
...
MainActivity:
import android.os.Bundle;
import android.view.MotionEvent;
import android.widget.AutoCompleteTextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
String[] items = {"C", "C++", "Java", "C#", "PHP", "JavaScript", "jQuery", "AJAX", "JSON"};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<DataClass> wordList = new ArrayList<>();
for (int i = 0; i < items.length; i++) {
DataClass data = new DataClass(i, items[i]);
wordList.add(data);
}
AutoCompleteAdapter<DataClass> adapter = new AutoCompleteAdapter<>(this,
R.layout.row_dropdown, R.id.text1, wordList);
//adapter.setDropDownViewResource(R.layout.row_dropdown);
AutoCompleteTV acTextView = findViewById(R.id.languages);
acTextView.setThreshold(1);
acTextView.setAdapter(adapter);
acTextView.setText("Java");
acTextView.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
((AutoCompleteTextView) v).showDropDown();
v.requestFocus();
v.performClick(); // Added to avoid warning "onTouch lambda should call View#performClick when a click is detected".
}
return false;
}
);
}
}

how to get data from database with parameter

I'm trying to show a list with the list items from my database having 2 parameters, but the list didn't show.
Here's my query database from php:
$query = mysql_query("SELECT d.model_name from detail_product d join brand b on d.id_brandtype = b.id_brand join biketype t on d.id_biketype = t.id_type where d.id_brandtype = 1 and d.id_biketype=1");
I've set the parameter there.
and here's my DAO:
public class info_xc_dao {
public final static int VIEW_POL_XC = 0;
private static final String URL_VIEW_POL_XC = ServerConfiguration.SERVER_URL
+ "polygon_xc.php";
private int Action;
pol_xc_Result pol_xc_Result;
public info_xc_dao(pol_xc_Result pol_xc_Result) {
// TODO Auto-generated constructor stub
this.pol_xc_Result = pol_xc_Result;
}
public void view_pol_xc() {
Action = VIEW_POL_XC;
new ConnectionHandler(connectionResult, null, URL_VIEW_POL_XC,
ConnectionHandler.GET);
}
public ConnectionResult connectionResult = new ConnectionResult() {
#Override
public void gotResult(String result, String message) {
// TODO Auto-generated method stub
if (result != null) {
switch (Action) {
case VIEW_POL_XC:
try {
JSONObject jsonObject = null;
jsonObject = new JSONObject(result);
int response = jsonObject.getInt("result");
if (response != 0) {
JSONArray jsonArray = new JSONArray(
jsonObject.getString("data"));
ArrayList<Entity_Detail_Product> arrayList = new ArrayList<Entity_Detail_Product>();
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject pol_xc = new JSONObject(jsonArray
.get(i).toString());
Entity_Detail_Product ent_detail_product = new Entity_Detail_Product();
ent_detail_product.setModel_name(pol_xc
.getString("model_name"));
arrayList.add(ent_detail_product);
}
pol_xc_Result.gotResult(arrayList, null, Action);
} else {
String error_message = jsonObject
.getString("message");
pol_xc_Result.gotResult(null, error_message,
Action);
}
} catch (JSONException e) {
// TODO: handle exception
pol_xc_Result.gotResult(null,
"Failed parsing data from database. Please try again.. "
+ e.getMessage(), Action);
Log.e("CON ERROR", e.getMessage());
}
}
} else {
pol_xc_Result.gotResult(null, message, Action);
Log.e("CON ERROR", message);
}
}
};
public static abstract class pol_xc_Result {
public abstract void gotResult(Object obj, String message, int action);
}
and here's my activity to call DAO.
public class Information_XC_Activity extends ListActivity {
WebView mWebView;
TextView tv;
private Adapter_DetailPolXC adapterDetailPolXC;
private ListView mainListView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.info_xc);
final ActionBar actionBar = (ActionBar) findViewById(R.id.actionbar);
actionBar.setTitle(getString(R.string.app_name));
int index = getIntent().getIntExtra("text", 0);
mWebView = (WebView) findViewById(R.id.webview_xc);
String temp = "<html><body>" + "<p align=\"justify\">"
+ getString(R.string.xc_info + index) + "</p> "
+ "</body></html>";
mWebView.loadData(temp, "text/html", "utf-8");
ArrayList<Entity_Detail_Product> arraylist = new ArrayList<Entity_Detail_Product>();
adapterDetailPolXC = new Adapter_DetailPolXC(this, R.layout.info_row,
arraylist);
setListAdapter(adapterDetailPolXC);
new info_xc_dao(response).view_pol_xc();
}
pol_xc_Result response = new pol_xc_Result() {
#Override
public void gotResult(Object obj, String message, int action) {
// TODO Auto-generated method stub
// actionBar.setProgressBarVisibility(View.INVISIBLE);
#SuppressWarnings("unchecked")
ArrayList<Entity_Detail_Product> arrayList = (ArrayList<Entity_Detail_Product>) obj;
AlertDialog.Builder builder = new AlertDialog.Builder(
Information_XC_Activity.this);
if (arrayList == null) {
builder.setIcon(R.drawable.alert_warning);
builder.setTitle("Error");
builder.setMessage("Data Not Found !");
builder.setNegativeButton("Ok",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
}
});
AlertDialog alert = builder.create();
alert.show();
} else {
for (Entity_Detail_Product entity_Detail_Product : arrayList) {
adapterDetailPolXC.add(entity_Detail_Product);
}
adapterDetailPolXC.notifyDataSetChanged();
}
}
};
private class Adapter_DetailPolXC extends
ArrayAdapter<Entity_Detail_Product> {
private ArrayList<Entity_Detail_Product> items;
public Adapter_DetailPolXC(Context context, int textViewResourceId,
ArrayList<Entity_Detail_Product> items) {
super(context, textViewResourceId, items);
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View v = convertView;
if (v == null) {
LayoutInflater layoutInflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = (View) layoutInflater.inflate(R.layout.info_xc, null);
}
Entity_Detail_Product adapter_Detail_Product = items.get(position);
if (adapter_Detail_Product != null) {
TextView textView = (TextView) v.findViewById(R.id.tv_test);
if (textView != null)
textView.setText(adapter_Detail_Product.getModel_name());
}
return v;
}
}
and here's my logcat :
09-22 12:41:30.239: W/IInputConnectionWrapper(12808): showStatusIcon on inactive InputConnection
09-22 12:41:33.129: E/HttpResponse(12808): {"result":1,"data":[{"0":"1","id_brand":"1","1":"Polygon","brand_name":"Polygon"},{"0":"2","id_brand":"2","1":"United Bike","brand_name":"United Bike"},{"0":"3","id_brand":"3","1":"WimCycle","brand_name":"WimCycle"}]}
09-22 12:41:35.009: E/HttpResponse(12808): {"result":1,"data":[{"0":"1","id_type":"1","1":"Cross Country (XC)","type_name":"Cross Country (XC)"},{"0":"2","id_type":"2","1":"Bicycle Motocross (BMX)","type_name":"Bicycle Motocross (BMX)"},{"0":"3","id_type":"3","1":"Free Ride (FR)","type_name":"Free Ride (FR)"},{"0":"4","id_type":"4","1":"DownHill (DH)","type_name":"DownHill (DH)"},{"0":"5","id_type":"5","1":"DirtJump (DJ)","type_name":"DirtJump (DJ)"},{"0":"6","id_type":"6","1":"Road Bike (RB)","type_name":"Road Bike (RB)"}]}
09-22 12:41:37.219: D/PhoneWindow(12808): couldn't save which view has focus because the focused view com.android.internal.policy.impl.PhoneWindow$DecorView#405440f0 has no id.
09-22 12:41:37.519: I/webclipboard(12808): clipservice: android.sec.clipboard.ClipboardExManager#405d4d40
09-22 12:41:37.839: V/webview(12808): OnSizeChanged: Enter
09-22 12:41:37.859: E/HttpResponse(12808): {"result":1,"data":[{"0":"1","id_type":"1","1":"Cross Country (XC)","type_name":"Cross Country (XC)"},{"0":"2","id_type":"2","1":"Bicycle Motocross (BMX)","type_name":"Bicycle Motocross (BMX)"},{"0":"3","id_type":"3","1":"Free Ride (FR)","type_name":"Free Ride (FR)"},{"0":"4","id_type":"4","1":"DownHill (DH)","type_name":"DownHill (DH)"},{"0":"5","id_type":"5","1":"DirtJump (DJ)","type_name":"DirtJump (DJ)"},{"0":"6","id_type":"6","1":"Road Bike (RB)","type_name":"Road Bike (RB)"}]}
09-22 12:41:38.169: E/CON ERROR(12808): No value for model_name
09-22 12:41:38.859: V/webview(12808): ZoomScale 3 mPreserveZoom: false
09-22 12:41:38.889: D/CONTEXT(12808): m_mainFrame->editor()->hasComposition not
09-22 12:41:38.959: D/CONTEXT(12808): m_mainFrame->editor()->hasComposition not

CheckboxCell, MultiSelectionModel unwantonly reset DataGrid's data

Using GWT 2.4...
I am building upon a complex Composite dual view/edit mode implementation that is backed GWT's DataGrid and MultiSelectionModel. My goal is for a user to be able to click a checkbox in each row that they'd like to post updates for.
Here's a screenshot from a semi-functional interface:
Note the selected (highlighted) rows.
Now the problem is that when I type something in any of the cells (e.g., the first row's $ cell under the $/Mw 1 composite cell header), then click that row's checkbox (or any other row's checkbox for that matter) to select or de-select, the value gets reset to the original value when the screen's data was first requested. Not desired behavior by any stretch!
Let's take a look at my custom implementation for the grid. (Excuse the length).
public abstract class ToggleableGrid<T extends Identifiable<?>> extends Composite {
private static final int CHKBOX_COLUMN_WIDTH = App.INSTANCE.checkboxColumnWidth();
private static final DisplayMode DEFAULT_MODE = DisplayMode.VIEW;
private ProvidesKey<T> keyProvider;
private DataGrid<T> grid;
private MultiSelectionModel<T> selectionModel;
private ListDataProvider<T> dataProvider;
private int tabIndex = 0;
public ToggleableGrid() {
final DataGridConfiguration config = new DefaultDataGridConfiguration();
initGrid(config);
}
public ToggleableGrid(DataGridConfiguration config) {
initGrid(config);
}
private void initGrid(DataGridConfiguration config) {
keyProvider = new ProvidesKey<T>() {
#Override
public Object getKey(T item) {
return item == null ? null : item.getId();
}
};
grid = new DataGrid<T>(config.getPageSize(), config.getResources(), keyProvider);
// Set the message to display when the table is empty.
grid.setEmptyTableWidget(new Label(UiMessages.INSTANCE.no_results()));
initWidget(grid);
setVisible(true);
}
public void setInput(List<T> content) {
setInput(content, DEFAULT_MODE);
}
public void setInput(List<T> content, DisplayMode mode) {
resetTableColumns();
if (isInEditMode(mode)) {
// Add a selection model so we can select cells
selectionModel = new MultiSelectionModel<T>(keyProvider);
grid.setSelectionModel(selectionModel, DefaultSelectionEventManager.<T> createCheckboxManager(0));
addRowSelector();
}
dataProvider = new ListDataProvider<T>(content);
final ListHandler<T> sortHandler = new ListHandler<T>(dataProvider.getList());
grid.addColumnSortHandler(sortHandler);
initializeStructure(constructMetadata(), sortHandler, mode);
dataProvider.addDataDisplay(grid);
}
// see https://stackoverflow.com/questions/3772480/remove-all-columns-from-a-celltable
// concrete classes are forced to maintain a handle on all columns added
private void resetTableColumns() {
for (final Column<T, ?> column: allColumns()) {
grid.removeColumn(column);
}
allColumns().clear();
}
protected boolean isInEditMode(DisplayMode currentDisplayMode) {
boolean result = false;
if (currentDisplayMode.equals(DisplayMode.EDIT)) {
result = true;
}
return result;
}
protected abstract Set<Column<T, ?>> allColumns();
protected abstract TableMetadata constructMetadata();
protected abstract void initializeStructure(TableMetadata metadata, ListHandler<T> sortHandler, DisplayMode mode);
protected void setColumnHorizontalAlignment(Column<T, ?> column, HorizontalAlignmentConstant alignment) {
column.setHorizontalAlignment(alignment);
}
// TODO figure out how to add a checkbox to column header that provides select/de-select all capability
// see https://stackoverflow.com/questions/6174689/gwt-celltable-programmatically-select-checkboxcell
protected void addRowSelector() {
final Column<T, Boolean> rowSelectColumn = new Column<T, Boolean>(new CheckboxCell(true, false)) {
#Override
public Boolean getValue(T value) {
Boolean result;
// check for null value and return null;
if(value == null || value.getId() == null) {
result = null;
} else { // get value from the selection model
result = selectionModel.isSelected(value);
}
return result;
}
};
addColumn(rowSelectColumn, UiMessages.INSTANCE.select());
setColumnWidth(rowSelectColumn, CHKBOX_COLUMN_WIDTH, Unit.PX);
setColumnHorizontalAlignment(rowSelectColumn, HasHorizontalAlignment.ALIGN_CENTER);
}
protected void setColumnWidth(Column<T, ?> column, int width, Unit unit) {
grid.setColumnWidth(column, width, unit);
}
protected void addColumn(Column<T, ?> column, String columnHeaderName) {
addColumn(column, columnHeaderName, HasHorizontalAlignment.ALIGN_RIGHT);
}
protected void addColumn(Column<T, ?> column, String columnHeaderName, HorizontalAlignmentConstant alignment) {
final SafeHtmlBuilder sb = new SafeHtmlBuilder();
final String divStart = "<div align=\""+ alignment.getTextAlignString() + "\" class=\"" +UiResources.INSTANCE.style().word_wrap() + "\">";
sb.appendHtmlConstant(divStart).appendEscaped(columnHeaderName).appendHtmlConstant("</div>");
final SafeHtml header = sb.toSafeHtml();
grid.addColumn(column, header);
allColumns().add(column);
}
protected CompositeCell<T> generateCompositeCell(final List<HasCell<T, ?>> hasCells) {
final CompositeCell<T> compositeCell = new CompositeCell<T>(hasCells) {
#Override
public void render(Context context, T value, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<table><tbody><tr>");
super.render(context, value, sb);
sb.appendHtmlConstant("</tr></tbody></table>");
}
#Override
protected Element getContainerElement(Element parent) {
// Return the first TR element in the table.
return parent.getFirstChildElement().getFirstChildElement().getFirstChildElement();
}
#Override
protected <X> void render(Context context, T value,
SafeHtmlBuilder sb, HasCell<T, X> hasCell) {
final Cell<X> cell = hasCell.getCell();
sb.appendHtmlConstant("<td>");
cell.render(context, hasCell.getValue(value), sb);
sb.appendHtmlConstant("</td>");
}
};
return compositeCell;
}
// FIXME not working quite the way we'd expect, index incremented within column for each row, not each row by column
protected int nextTabIndex() {
tabIndex++;
return tabIndex;
}
protected AbstractCellTable<T> getGrid() {
return grid;
}
/**
* Gets the selected (row(s) of) data from grid (used in edit mode)
* #return the selected data (as per selection model)
*/
public List<T> getSelectedData() {
final List<T> data = new ArrayList<T>();
data.addAll(selectionModel.getSelectedSet());
return data;
}
/**
* Gets all (row(s) of) data in grid (used in edit mode)
* #return all data as list
*/
public List<T> getAllData() {
return dataProvider.getList();
}
/**
* Clears the currently selected (row(s) of) data (used in edit mode)
*/
public void clearSelectedData() {
selectionModel.clear();
grid.redraw();
}
}
So, the interesting methods to stare at above (I think) are setInput, generateCompositeCell and addRowSelector.
We initialize the grid with List data and set a display mode in setInput. It's here as well that the selection model is initialized. It uses GWT's DefaultSelectionEventManager createCheckboxManager().
I've been trying to grok the event model, but it eludes me. I've visited the following sources online, but have come up short on avenues to solving this problem.
-- https://groups.google.com/forum/?fromgroups#!topic/google-web-toolkit/k5sfURxDaVg
AbstractInputCell's getConsumedEventsImpl adds focus, blur and keydown, so this (I believe) is not a track I need to explore
-- GWT CellTable programmatically select CheckBoxCell
The various ways you can instantiate a CheckBoxCell got me curious, and I've tried many constructor argument permutations, but the one I settled on (true, false) is (I believe) the right one
Agreeing here and now (before being reprimanded) that there's perhaps some unnecessary complexity in my implementation, but I am looking for guidance nonetheless. Thanks!
Update
If it helps here's an impl of the aforementioned ToggleableGrid. If anything it gives you more detail on what goes into each CompositeCell. For details on AbstractValidatableColumn and ValidatableInputCell, see: In search of a GWT validation example... where art thou?.
public class EnergyOfferGrid extends ToggleableGrid<EnergyOfferDTO> {
public EnergyOfferGrid() {
super();
}
public EnergyOfferGrid(DataGridConfiguration config) {
super(config);
}
private static final int MAX_NUMBER_OF_MW_PRICE_POINTS = App.INSTANCE.maxNoOfMwPricePoints();
private Set<Column<EnergyOfferDTO, ?>> columns = new HashSet<Column<EnergyOfferDTO, ?>>();
#Override
protected Set<Column<EnergyOfferDTO, ?>> allColumns() {
return columns;
}
#Override
protected TableMetadata constructMetadata() {
final TableMetadata metadata = new TableMetadata();
// TODO Consider a predefined set of ReferenceData to be held in a common package
// Use Slope
metadata.addColumnMetadata(UiMessages.INSTANCE.use_slope(), new String[] {UiMessages.INSTANCE.yes(), UiMessages.INSTANCE.no()}, new String[] {"true", "false"});
return metadata;
}
#Override
protected void initializeStructure(TableMetadata metadata, ListHandler<EnergyOfferDTO> sortHandler, DisplayMode currentDisplayMode) {
addHourColumn(sortHandler);
addUseSlopeColumn(metadata, sortHandler, currentDisplayMode);
for (int i = 0; i < MAX_NUMBER_OF_MW_PRICE_POINTS; i++) { // zero-based indexing
addPriceMwColumn(i, currentDisplayMode);
}
}
protected void addHourColumn(ListHandler<EnergyOfferDTO> sortHandler) {
final Column<EnergyOfferDTO, String> hourColumn = new Column<EnergyOfferDTO, String>(new TextCell()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
String result = "";
if (energyOffer.getId() != null) {
final String isoDateTime = energyOffer.getId().getOperatingHour();
if (isoDateTime != null && !isoDateTime.isEmpty()) {
final Date dateTime = CSTimeUtil.isoToDate(isoDateTime);
if (dateTime != null) {
result = CSTimeUtil.dateToHour(dateTime);
}
}
}
return result;
}
};
hourColumn.setSortable(true);
sortHandler.setComparator(hourColumn, new Comparator<EnergyOfferDTO>() {
#Override
public int compare(EnergyOfferDTO eo1, EnergyOfferDTO eo2) {
final String date1 = eo1.getId() != null ? eo1.getId().getOperatingHour() : "";
final String date2 = eo2.getId() != null ? eo2.getId().getOperatingHour() : "";
return date1.compareTo(date2);
}
});
// We know that the data is sorted by hour by default.
getGrid(). getColumnSortList().push(hourColumn);
addColumn(hourColumn, UiMessages.INSTANCE.hour());
setColumnWidth(hourColumn, 45, Unit.PX);
setColumnHorizontalAlignment(hourColumn, HasHorizontalAlignment.ALIGN_RIGHT);
}
protected void addUseSlopeColumn(TableMetadata metadata, ListHandler<EnergyOfferDTO> sortHandler, DisplayMode currentDisplayMode) {
final ReferenceData refData = metadata.allColumnMetadata().get(UiMessages.INSTANCE.use_slope());
Column<EnergyOfferDTO, String> useSlopeColumn;
Cell<String> cell;
if (isInEditMode(currentDisplayMode)) {
cell = new ReferenceDataBackedSelectionCell(refData);
} else {
cell = new TextCell();
}
useSlopeColumn = new Column<EnergyOfferDTO, String>(cell) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
return refData.getDisplayValueForSubmitValue(Boolean.toString(energyOffer.isSlopeUsed()));
}
};
useSlopeColumn.setSortable(true);
sortHandler.setComparator(useSlopeColumn, new Comparator<EnergyOfferDTO>() {
#Override
public int compare(EnergyOfferDTO eo1, EnergyOfferDTO eo2) {
final String slopeUsed1 = String.valueOf(eo1.isSlopeUsed());
final String slopeUsed2 = String.valueOf(eo1.isSlopeUsed());
return slopeUsed1.compareTo(slopeUsed2);
}
});
addColumn(useSlopeColumn, UiMessages.INSTANCE.use_slope());
setColumnWidth(useSlopeColumn, 75, Unit.PX);
setColumnHorizontalAlignment(useSlopeColumn, HasHorizontalAlignment.ALIGN_RIGHT);
}
protected void addPriceMwColumn(final int colIndex, DisplayMode currentDisplayMode) {
// Construct a composite cell for energy offers that includes a pair of text inputs
final List<HasCell<EnergyOfferDTO, ?>> columns = new ArrayList<
HasCell<EnergyOfferDTO, ?>>();
// this DTO is passed along so that price and mw values for new entries are kept together
final OfferPriceMwPair newOfferPriceMwPair = new OfferPriceMwPair();
// Price
final Column<EnergyOfferDTO, String> priceColumn = generatePriceColumn(colIndex, newOfferPriceMwPair, currentDisplayMode);
columns.add(priceColumn);
// MW
final Column<EnergyOfferDTO, String> mwColumn = generateMwColumn(colIndex, newOfferPriceMwPair, currentDisplayMode);
columns.add(mwColumn);
// Composite
final CompositeCell<EnergyOfferDTO> priceMwColumnInnards = generateCompositeCell(columns);
final IdentityColumn<EnergyOfferDTO> priceMwColumn = new IdentityColumn<EnergyOfferDTO>(priceMwColumnInnards);
final StringBuilder colHeader = new StringBuilder();
colHeader.append(UiMessages.INSTANCE.price_mw_header()).append(" ").append(String.valueOf(colIndex + 1));
addColumn(priceMwColumn, colHeader.toString());
setColumnWidth(priceMwColumn, 7, Unit.EM);
setColumnHorizontalAlignment(priceMwColumn, HasHorizontalAlignment.ALIGN_RIGHT);
}
protected Column<EnergyOfferDTO, String> generatePriceColumn(final int colIndex, final OfferPriceMwPair newOfferPriceMwPair, DisplayMode currentDisplayMode) {
Column<EnergyOfferDTO, String> priceColumn;
if (isInEditMode(currentDisplayMode)) {
priceColumn = new BigDecimalValidatableColumn<EnergyOfferDTO, OfferPriceMwPair>(nextTabIndex(), getGrid()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
return obtainPriceValue(colIndex, energyOffer, false);
}
#Override
public void doUpdate(int index, EnergyOfferDTO energyOffer, String value) {
if (value != null && !value.isEmpty()) {
// number format exceptions should be caught and handled by event bus's handle method
final double valueAsDouble = NumberFormat.getDecimalFormat().parse(value);
final BigDecimal price = BigDecimal.valueOf(valueAsDouble);
final List<OfferPriceMwPair> offerPriceCurve = energyOffer.getCurve();
final OfferPriceMwPair offerPriceMwPair = offerPriceCurve.get(colIndex);
if (offerPriceMwPair == null) { // we have a new price value
newOfferPriceMwPair.setPrice(price);
offerPriceCurve.add(newOfferPriceMwPair);
} else {
offerPriceMwPair.setPrice(price);
}
}
}
#Override
protected String getPropertyName() {
return "price";
}
#Override
protected Class<OfferPriceMwPair> getPropertyOwner() {
return OfferPriceMwPair.class;
}
};
} else {
priceColumn = new Column<EnergyOfferDTO, String>(new TextCell()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
final String result = obtainPriceValue(colIndex, energyOffer, true);
return result;
}
};
}
return priceColumn;
}
private String obtainPriceValue(final int colIndex, EnergyOfferDTO energyOffer, boolean withCurrency) {
String result = "";
if (energyOffer != null) {
final List<OfferPriceMwPair> offerPriceCurve = energyOffer.getCurve();
final int numberOfPairs = offerPriceCurve.size();
if (colIndex < numberOfPairs) {
final OfferPriceMwPair offerPriceMwPair = offerPriceCurve.get(colIndex);
if (offerPriceMwPair != null) {
final BigDecimal price = offerPriceMwPair.getPrice();
if (price != null) {
final double value = price.doubleValue();
if (withCurrency) {
result = NumberFormat.getCurrencyFormat().format(value);
} else {
result = NumberFormat.getDecimalFormat().format(value);
}
}
}
}
}
return result;
}
protected Column<EnergyOfferDTO, String> generateMwColumn(final int colIndex, final OfferPriceMwPair newOfferPriceMwPair, DisplayMode currentDisplayMode) {
Column<EnergyOfferDTO, String> mwColumn;
if (isInEditMode(currentDisplayMode)) {
mwColumn = new BigDecimalValidatableColumn<EnergyOfferDTO, PriceMwPair>(nextTabIndex(), getGrid()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
return obtainMwValue(colIndex, energyOffer);
}
#Override
public void doUpdate(int index, EnergyOfferDTO energyOffer, String value) {
if (value != null && !value.isEmpty()) {
// number format exceptions should be caught and handled by event bus's handle method
final double valueAsDouble = NumberFormat.getDecimalFormat().parse(value);
final BigDecimal mw = BigDecimal.valueOf(valueAsDouble);
final List<OfferPriceMwPair> offerPriceCurve = energyOffer.getCurve();
final OfferPriceMwPair offerPriceMwPair = offerPriceCurve.get(colIndex);
if (offerPriceMwPair == null) { // we have a new price value
newOfferPriceMwPair.setMw(mw);
offerPriceCurve.add(newOfferPriceMwPair);
} else {
offerPriceMwPair.setMw(mw);
}
}
}
#Override
protected String getPropertyName() {
return "mw";
}
#Override
protected Class<PriceMwPair> getPropertyOwner() {
return PriceMwPair.class;
}
};
} else {
mwColumn = new Column<EnergyOfferDTO, String>(new TextCell()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
final String result = obtainMwValue(colIndex, energyOffer);
return result;
}
};
}
return mwColumn;
}
private String obtainMwValue(final int colIndex, EnergyOfferDTO energyOffer) {
String result = "";
if (energyOffer != null) {
final List<OfferPriceMwPair> offerPriceCurve = energyOffer.getCurve();
final int numberOfPairs = offerPriceCurve.size();
if (colIndex < numberOfPairs) {
final PriceMwPair offerPriceMwPair = offerPriceCurve.get(colIndex);
if (offerPriceMwPair != null) {
final BigDecimal mw = offerPriceMwPair.getMw();
if (mw != null) {
result = NumberFormat.getDecimalFormat().format(mw);
}
}
}
}
return result;
}
}
All that custom work w.r.t. WrapperCell and CompositeValidatableColumn was unnecessary.
It turns out that there's a way you should not construct CompositeCells. See http://code.google.com/p/google-web-toolkit/issues/detail?id=5714. My CompositeCells were not receiving events. So, I changed the way I construct them in ToggleableGrid.
protected CompositeCell<T> generateCompositeCell(final List<HasCell<T, String>> hasCells) {
final CompositeCell<T> compositeCell = new CompositeCell<T>(hasCells) {
// to not run afoul of http://code.google.com/p/google-web-toolkit/issues/detail?id=5714
#Override
public void render(Context context, T value, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<div style=\"display: inline\">");
super.render(context, value, sb);
sb.appendHtmlConstant("</div>");
}
#Override
protected Element getContainerElement(Element parent) {
// Return the first element in the DIV.
return parent.getFirstChildElement();
}
};
return compositeCell;
}
After that change and incorporating my other validation-oriented classes: ValidatableFieldUpdater, AbstractValidatableColumn (and derivatives), ValidatableInputField and ConversionResult, life couldn't be more grand!