Issue with ViewHolder in ArrayAdapter - Android - android

I have a custom adapter that renders two (2) checkboxes, a picture and the name of the client. All the information needed for the adapter is fetched from an ArratList that contains the Client class.
Every row needs to have both checkboxes checked (selected) for the client in order to process the purchase order, in case that a particular client has one checkbox checked-off and the other checkbox not, that raises a flag as MISMATCH. To make a valid order both checkboxes need to be checked-off.
We are implementing a button for verification, which will find any mismatch in the adapter and then hightlight the mismatches.
EDITION: After pressing the verificationBtn I am able to identify if any row has mismatch on checkboxes, for example, if checkbox1 was checked and checkbox2 not. that will mark the row as mismatch. I am using the position of my checkboxes based on clientList that is an arraylist of List clientList.
QUESTION: How can I get the position that the viewHolder has in order compare against the clientList position? Is there any way I can force the viewHolder to store the position and get it back and make the comparison with cli.getClient_number() ?
So far I have tested two different ways with no luck:
Method 1:
viewHolder.clientName.setBackgroundColor((Interger.parseInt(cli.getClient_number()) ) == position ? Color.GREEN : Color.TRANSPARENT);
Method 2
viewHolder.clientName.setBackgroundColor(Color.GREEN);
here the code that I am implementing.
// This goes in my main Client Activity
Button verificationBtn = (Button) findViewById(R.id.verificationBtn);
verificationBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
buffer.setLength(0);
mismatchTv.setText("");
for (Client cli : clientList) {
if (cli.isCheckboxOneSelected() != cli.isCheckboxTwoSelected()) {
//We had defined above the following buffer = new StringBuffer();
buffer.append((ah.parseInt(cli.getClient_number(), 0) - 1) + ", ");
cli.setMismatch(true);
//We are passing here the ID that correspond to the client mismatch
list_adapter.setBackgroundColor(Integer.parseInt(cli.getClient_number()) - 1);
setListAdapter(list_adapter);
Log.w("cli.getClient_number() ", String.valueOf(Integer.parseInt(cli.getClient_number()) - 1));
}
}
// We display any mismatch on a TextView on top of the screen
if (buffer.length() != 0) {
//This is a TextView on top of the screen
mismatchTv.setText("Error en Client(s) "
+ buffer.toString());
}
// This goes inside of the ClientArrayAdapter
public void setBackgroundColor(int position) {
Log.w("inside of setBackgroundColor method", "True");
switchIndex = 1;
positionFetched = position;
}
// This goes inside of the ClientArrayAdapter
// and inside the body of public View getView(int position, View convertView, ViewGroup parent) {
switch (switchIndex) {
case 1:
viewHolder.cbone
.setButtonDrawable(R.drawable.btn_checkbox_selector);
viewHolder.cbtwo
.setButtonDrawable(R.drawable.btn_checkbox_selector);
Log.w("switch 1 was called ", "True");
for (Client cli : clientList) {
if (cli.isCheckboxOneSelected() != cli.isCheckboxTwoSelected()) {
Client cli = getItem(positionFetched);
if (cli.isMismatch()) {
cli.setColor(Color.BLACK);
Log.e("if (cli.isMismatch()) ", "");
//HERE WE ARE TRYING TO HIGHLIGHT THE ROW WITH MISMATCH
//WHY THIS LINE DOES NOT WORK?
//THE ISSUE THAT I AM GETTING IS THAT I CANNOT CONTROL WHAT ROW TO AFFECT
//IN THE VIEW HOLDER
viewHolder.clientName.setBackgroundColor(Color.GREEN);
}
}
}
break;
default:
viewHolder.cbone.setButtonDrawable(R.drawable.disabled_cb);
viewHolder.cbtwo.setButtonDrawable(R.drawable.disabled_cb);
break;
}
// This goes inside of the ClientArrayAdapter
static class ViewHolder {
public TextView clientName;
public TextView clientNumber;
public ImageView imageView;
public CheckBox cbtwo;
public CheckBox cbone;
public int position;
}

After three days trying to figure out what's wrong with this code, I finally found the solution moving the Method #1 just at the very end of the getView method. :-)

Related

How could I know which "TextView" is clicked

I was creating a dynamic Text View and I need to know which one of those Text views was clicked by the user i read that i need to use getTag() method but it keeps return null when i try it this is my activity code in java:
for(int i=0;i<size;i++){
TextView temp = new TextView(this);
temp.setId(i);
temp.setId(i);
String s = "";
temp.setText(s);
temp.setTextColor(Color.RED);
mylieniarlayout.addView(temp);
tv[i] = temp;
}
final TextView answertv = findViewById(R.id.answertv);
mylieniarlayout.setOnClickListener(new View.OnClickListener() {
#SuppressLint("SetTextI18n")
#Override
public void onClick(View v) {
Toast.makeText(getBaseContext(),"ID: "+ v.getTag(), Toast.LENGTH_SHORT).show();
answertv.setText("Clicked ID: " + v.getTag());
}
});
Firstly, setTag() and getTag() methods on view are not used for identifying the view. We use getId() for identifying the view. Please read document here,
getTag on android developers
Secondly, your onClickListner is on layout which won't give you the selected id of textView. In your code you are setting an ID for the text view and trying to fetch a tag which is a mistake.
onClickListener should be on the textview of which you want a tag.
While creating a textView set the tag and then you will get the tag for that textview.
You can try if else condition
Like:
if(v.getTag() == 0){
//Show position 0 here
} else {
//Other position
}
Hope you got your answer

How to get Spinner dropdown view to draw on it or the view of one of the items in the dropdown?

I have a need from the product team to get the instance of the currently open Spinner dropdown view and alter it or present a Showcase above one of it's dropdown items. After going over some questions here and other articles like this one:
Android Spinner - How to make dropdown view transparent?
and this one:
How to customize the Spinner dropdown view
I'm getting the feeling that the only access I have to the dropdown is using the xml parameters or the Spinner adapter. Yet I decided to ask the question anyway to relax my superior.
Another thing that might be helpful in my case is to get notified when the Spinner is expended, and find the opened popup instance as a result of this notification, but following this question:
Spinner: get state or get notified when opens
It looks like this is also can't be done without extending the Spinner into a custom view, something that in my case is not possible, as I need to do it from the SDK side that I'm writing for use on a standard Spinner.
Has any one here dealt with a Spinner and managed to get the view instance of the dropdown or even better the view instance of one of the dropdown items? I would be grateful if you can direct me in the right direction on how this can be achieved?
UPDATE: I have managed to get the view hierarchy of the spinner dropdown using the following code:
//Function to get all available windows of the application using reflection
private void logRootViews() {
try {
Class wmgClass = Class.forName("android.view.WindowManagerGlobal");
Object wmgInstnace = wmgClass.getMethod("getInstance").invoke(null, (Object[])null);
Method getViewRootNames = wmgClass.getMethod("getViewRootNames");
Method getRootView = wmgClass.getMethod("getRootView", String.class);
String[] rootViewNames = (String[])getViewRootNames.invoke(wmgInstnace, (Object[])null);
for(String viewName : rootViewNames) {
View rootView = (View)getRootView.invoke(wmgInstnace, viewName);
Log.i(TAG, "Found root view: " + viewName + ": " + rootView);
getViewHierarchy(rootView);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//Functions to get hierarchy
private void getViewHierarchy(View view) {
//This is how I start recursion to get view hierarchy
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
dumpViewHierarchyWithProperties(group, 0);
} else {
dumpViewWithProperties(view, 0);
}
}
private void dumpViewHierarchyWithProperties(ViewGroup group, int level) {
if (!dumpViewWithProperties(group, level)) {
return;
}
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
if (view instanceof ViewGroup) {
dumpViewHierarchyWithProperties((ViewGroup) view, level + 1);
} else {
dumpViewWithProperties(view, level + 1);
}
}
}
private boolean dumpViewWithProperties(View view, int level) {
//Add to view Hierarchy.
if (view instanceof TextView) {
Log.d(TAG, "TextView from hierarchy dumped: " + view.toString() + " with text: " + ((TextView) view).getText().toString() + " ,in Level: " + level);
} else {
Log.d(TAG, "View from hierarchy dumped: " + view.toString() + " ,in Level: " + level);
}
return true;
}
The problem is that to get all the decor windows of the application I need to use reflection, which is something I can't do in the SDK I'm writing, more over as I understand I use here a private API which can be changed at any moment and as I understood Google blocks applications from the store which do use a private API.
So the updated question is: Is there a way to do the same operation without reflection and private API?

How to change background color of click view android using base Adapter

I want to set the background color of clicked TextView included in LinearLayout. But I am unable to set the respective background. I faced not
clear previous clicked background . if I clicked all then set all background color.
Would you suggest me ,
how to set clickable TextView included in LinearLayout background.
Here is my sample code:
holder.txtName = (TextView) convertView.findViewById(R.id.row_cell_text_dummy_multilevel);
holder.l_select = (LinearLayout) convertView.findViewById(R.id.linear_select);
holder.txtName.setTag(position);
holder.txtName.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
select_pos=(Integer) holder.txtName.getTag();
if (position==select_pos) {
holder.l_select.setTextColor(Color.RED);
}else {
holder.l_select.setTextColor(Color.WHITE);
}
}
});
I managed to get this much done, though you will have to do some homework and find out how to reflect the changes yourself. At the moment, the change gets reflected only when the view is scrolled.
But i hope it helps you.
1.declare a static variable that sets the position clicked on
private static int selectedPostion;
2.set the selectedPosition's value to -1 in the constructor
3.in the getView method in the onclickListener do this:
int value = (Integer)((TextView)v).getTag();
Log.e("tag","(TextView)v).getTag() : " + value);
Log.e("tag", "position : " + position);
if(value == position) {
selectedPostion = position;
}else {
selectedPostion = -1;
}
4.Under the onClick code entirely before return view write this:
if(selectedPostion == position) {
view.setBackgroundColor(mContext.getResources().getColor(R.color.even_color));
// or holder.l_select.setTextColor(Color.RED);
}else {
view.setBackgroundColor(mContext.getResources().getColor(android.R.color.white));
// or holder.l_select.setTextColor(Color.WHITE);
}
Hope it helps!

Event TableFixHeader

I download a custom table from this site.
I want to show the value of the cell I click.
TableFixHeaders table = (TableFixHeaders) findViewById(R.id.table_1);
table.setAdapter(new MyAdapter(this));
table.setOnLongClickListener(new OnLongClickListener(){
#Override
public boolean onLongClick(View v) {
TextView Cl = (TextView) table.getChildAt(1);
Toast.makeText(this, Cl.getText().toString(), Toast.LENGTH_LONG).show();
return false;
}
});
The problem is I can't intercept any event in my main activity.
I also have downloaded this very nice widget with scrolling columns and rows. And I also needed to get a response on a click at a certain cell. It didn't come, but I found the reason here:
In TableFixHeaders.java, module OnTouchEvent(MotionEvent event) add this line at the beginning:
super.onTouchEvent(event);
This call will make sure that other touch methods gets called! So it should also work for longclicks as well!
I have added my click-handler to the specific view (cell) in module GetView(int row, int column, View convertView, ViewGroup parent) when convertView is inflated first time. Then I an able to have different handlers to different cells (columns-headers, data-cells etc).
Here is the detailed code:
private View getBody(final int row,final int column, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.item_table, parent, false);
}
/*if(row == 8){
convertView.setBackgroundResource(R.color.row8); //if row8 need different colour
}*/else {
//Change table color using bg_table_col
convertView.setBackgroundResource(row % 2 == 0 ? R.drawable.bg_table_color1 : R.drawable.bg_table_color2);
}
((TextView) convertView.findViewById(android.R.id.text1)).setText(records.get(row).getItem(column + 1));
convertView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
detailsearchPaperID = records.get(row).getItem(0);
final String S;
S = detailsearchPaperID + " : " + records.get(row).getItem(column + 1);
Log.d(TAG, S);
Toast.makeText(getApplicationContext(), S, Toast.LENGTH_SHORT).show();
//...
// we will not start intent here but after performing search
return true;
}
});
return convertView;
}
If you add the listener as explained in previous post (when convertView is inflated first time), this will work only on the first view, before performing any scroll.
My code works even if you have performed scroll. Of course you need to add
super.onTouchEvent(event);
as explained earlier
I'm using this wrapper for InqBarna's library.
This is a code snippet of how to achieve the click events on table body.
You can see more ellaborated examples here.
TableFixHeaderAdapter.ClickListener<List<String>, BasicCellViewGroup> clickListenerBody = new TableFixHeaderAdapter.ClickListener<List<String>, BasicCellViewGroup>() {
#Override
public void onClickItem(List<String> array, BasicCellViewGroup viewGroup, int row, int column) {
viewGroup.vg_root.setBackgroundColor(ContextCompat.getColor(context, R.color.colorYellow));
Snackbar.make(viewGroup, "Click on " + viewGroup.textView.getText() + " (" + row + "," + column + ")", Snackbar.LENGTH_SHORT).show();
}
};
and so on for the rest of the table cells:
adapter.setClickListenerFirstHeader(clickListenerHeader);
adapter.setLongClickListenerFirstHeader(longClickListenerHeader);
adapter.setClickListenerHeader(clickListenerHeader);
adapter.setLongClickListenerHeader(longClickListenerHeader);
adapter.setClickListenerFirstBody(clickListenerBody);
adapter.setLongClickListenerFirstBody(longClickListenerBody);
adapter.setClickListenerBody(clickListenerBody);
adapter.setLongClickListenerBody(longClickListenerBody);
adapter.setClickListenerSection(clickListenerBody);
adapter.setLongClickListenerSection(longClickListenerBody);
Hope it helps you.

Android Gallery visible ImageView is null?

I want to draw a check mark for the image view I click on and uncheck the imageview I clicked on before using the following code snip. I store last checked position in mDeviceAdapter. When I try to uncheck old position, the image view always gives null even for the partial visible image view. I am really confused because I thought only invisible one is recycled... Newbie in Android and any comment is appreciated.
public void CheckableImageView#setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
invalidate();
}
}
mDeviceGallery.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
CheckableImageView viewToCheck = (CheckableImageView) view;
if (!viewToCheck.isChecked()) {
int oldCheckedPosition = mDeviceAdapter
.getCheckedPosition();
mDeviceAdapter.setCheckedPosition(position);
View checkedView = mDeviceGallery
.getChildAt(oldCheckedPosition);
Log.d(TAG, "old position="+oldCheckedPosition + "old view="+checkedView);
if (checkedView != null) {
((CheckableImageView) checkedView)
.setChecked(false);
Log.d(TAG, "uncheck position="
+ oldCheckedPosition);
}
viewToCheck.setChecked(true);
That's not the right approach.
You need to add to your data type a boolean field (i.e mIsChecked).
On the onItemClick method set the value of that variable to true and keep its INDEX as a member of the adapter. When another item is clicked set the value of that item to true and set the value of the saved one to false (change the value of the datatype in you ArrayList in the INDEX you stored in the previous click).
Now, in the getView() method, you must have if/else statement. Something like:
if (item.isChecked())
{
checkedView.setChecked(true);
}
else
{
checkedView.setChecked(false);
}
Example to the onClick method: (just a general direction)
if (item.isChecked())
{
checkedView.setChecked(false);
yourList.get(position).setChecked(true);
yourList.get(mLastCheckedIndex).setChecked(false);
mLastCheckedIndex = position;
notifyDataSetChanged();
}
else
{
//same but opposite.
}
Hope this helps!

Categories

Resources