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.
Related
I am facing some problem using a listview to highlight a single item. I've created an anonymous adapter as in the code below but when the list is displayed several items have the highlight/bold. The log trace shows the bold/background only being called once and yet several items have the bold but only the correct one has the background color set. I separated the background color setting for debugging but the results are the same if I use only one if(position==selection) check.
Note that this is not an interactive listview, no onclicklistener needed.
I would greatly appreciate any help. Thank you in advance.
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, times) {
#Override
public View getView(int position, View cview, ViewGroup group) {
int count = 0; //used for debugging
View view = super.getView(position, cview, group);
TextView tv = (TextView)view.findViewById(android.R.id.text1);
Typeface tf = tv.getTypeface();
Log.d("log", "position = " + position);
if(position == selection) {
tv.setTypeface(tf, Typeface.BOLD);
Log.d("log", "BOLD" + position + "count=" + (count ++));
} else {
tv.setTypeface(tf, Typeface.NORMAL);
}
if(position == selection) {
view.setBackgroundColor(Color.LTGRAY);
Log.d("log", "Bkgnd " + position + "count: " + count);
} else {
view.setBackgroundColor(Color.WHITE);
}
return view;
}
};
I just wanted to add to this in case someone checks it. A good solution is as follows:
public View (int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.mylayout, parent, false);
}
TextView textView = convertView.findViewById(R.id.mytextview);
if(position == selection) {
textView.setBackgroundColor(Color.parseColor("#88FFFFFF");
} else {
textView.setBackgroundColor(Color.parseColor("#00000000");
}
return convertView;
}
What this does is set the background color to white with 50% alpha for the selection and 0% alpha, transparent, for the non-selection. This makes the selected item a little brighter but the same color.
A similar effect can be found by setting android:listSelector to "#88FFFFFF" in xml but doing it in the getView() function allows one to highlight more than one element differently while keeping the same color theme. I used this in my program to highlight some items in a listview with "#88000000" a darker item plus the "#88FFFFFF" for the selected item.
anyway, not sure if anyone will come across this but I found it helpful once I figured it out
Apparently, there is some "magic" with typefaces. I've changed the part that is responsible for setting the typeface as follows:
if(position == selection) {
tv.setTypeface(Typeface.create(tf, Typeface.BOLD));
}
else {
tv.setTypeface(Typeface.create(tf, Typeface.NORMAL));
}
and it works as expected.
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?
I am using nhaarman's ListviewAnimation library https://github.com/nhaarman/ListViewAnimations which works great.
But I am facing following issues:
The main problem I am facing is, I am not able to debug my code. I have directly copy/pasted the four required libraries into libs folder. Placing a debug point inside any of the listview methods like onItemLongClick() does not work.
The second problem is, drag-drop listView is not working in my code. Whenever I try to drag any list item, on dropping the list item, the item takes the same position from which it was dragged.
Here's the code I have used:
listview.enableDragAndDrop();
listview.setDraggableManager(new TouchViewDraggableManager(
R.id.list_row_draganddrop_textview));
listview.setOnItemMovedListener(this);
listview.setOnItemLongClickListener(this);
#Override
public void onItemMoved(final int originalPosition, final int newPosition) {
if (mToast != null) {
mToast.cancel();
}
mToast = Toast.makeText(getApplicationContext(), "Moved"
+ swingBottomInAnimationAdapter.getItem(newPosition)
+ newPosition, Toast.LENGTH_SHORT);
mToast.show();
}
#Override
public boolean onItemLongClick(final AdapterView<?> parent,
final View view, final int position, final long id) {
if (listview != null) {
listview.startDragging(position - listview.getHeaderViewsCount());
}
return true;
}
Whenever I try to drag any list item, on dropping the list item, the item takes the same position from which it was dragged.
Of course. Handling the change in position is your responsibility, and you should take care of it inside the onItemMoved callback:
#Override
public void onItemMoved(final int originalPosition, final int newPosition) {
if (mToast != null) {
mToast.cancel();
}
mToast = Toast.makeText(getApplicationContext(), "Moved"
+ swingBottomInAnimationAdapter.getItem(newPosition)
+ newPosition, Toast.LENGTH_SHORT);
mToast.show();
// Adapt the following to your implementation
if (originalPosition != newPosition) {
YourObject item = (YourObject) yourAdapter.getItem(originalPosition);
yourAdapter.moveItem(item, newPosition);
}
}
The method mentioned above would look something like:
public void moveItem(YourObject item, int newIndex) {
if (mEntries != null) {
mEntries.remove(item);
mEntries.add(newIndex, item);
notifyDataSetChanged();
}
}
If you go through the source code, you'll see that what you are dragging around is a Bitmap. The list item is sitting at its original position.
For others having the same problem - Niek Haarman has answered this question on GitHub here.
Don't see GitHub going down soon, but as it is good tone to paste the answer too, here it is:
#Override
public long getItemId(int position) {
return position;
}
#Override
public boolean hasStableIds() {
return true;
}
position is not a stable id here. You need a stable id for the item
which does not depend on the position.
use
import com.nhaarman.listviewanimations.ArrayAdapter;
instead of
import android.widget.ArrayAdapter;
that is the reason it doesn't calling onItemMoved
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. :-)
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!