I have ListView at my fragment where I try to select some items by LongClick.
At my screen I can see 8 items ( on another smartphone I can see 6 items.) When I have a lot items in my ListView ( for example 23) and I LongClick on a first item as a result I see changed image, but if I scroll down I can see that 10 and 19 items has changed image too ( like they has been checked ). At the another smartphone after LongClick the first item I see change 8 and 16 too and etc). As you can see I get change ListView items images just after the same numbers of items as can present my smartphone. The real status of "additional" items not changed, only their image view. It's the strange behavior of ListView, which duplicate changing image of items view at each group ( group means the number ListView items a multiple the number of items which the smartphone can display simultaneously)
Whats wrong in my code or how to avoid this unexpected behavior of ListView?
Thanx
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import ru.someCode.R;
public class ListItems extends ListFragment {
private ListView lv;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_list, null);
lv = getListView();
return rootView;
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapter, View view,
int position, long id) {
Log.d("MY", "Checked");
ImageView imageView = ((ImageView) view.findViewById(R.id.operImg));
ImageView imageViewCheck = ((ImageView) view.findViewById(R.id.operImgCheck));
if (lv.isItemChecked(position)) {
lv.setItemChecked(position, false);
imageView.setVisibility(View.VISIBLE);
imageViewCheck.setVisibility(View.GONE);
} else {
lv.setItemChecked(position, true);
imageView.setVisibility(View.INVISIBLE);
imageViewCheck.setVisibility(View.VISIBLE);
}
return true;
}
});
}
}
---- fragment_list.xml ----
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="6dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/listContainer">
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dividerHeight="1dip"
android:divider="#color/listDev"
android:footerDividersEnabled="true"/>
<TextView
android:id="#id/android:empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="16sp"
android:gravity="center" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/progressContainer"
android:layout_centerInParent="true"
android:gravity="center"
android:visibility="gone">
<ProgressBar style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
---- adapter code part ---
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.history_list_group, null);
holder = findViewsById(convertView);
convertView.setTag(holder);
if (lv == null) {
lv = (ListView) parent;
}
} else {
holder = (ViewHolder) convertView.getTag();
}
SetDataView(holder, position);
return convertView;
}
---- history_list_group.xml --
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/circle"
android:id="#+id/operImgLayout"
android:layout_gravity="center_vertical"
android:layout_margin="6dp">
<ru.phone4pay.phone4pay.extlib.MLRoundedImageView
android:id="#+id/operImg"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/ic_action_1"/>
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:id="#+id/operImgCheck"
android:src="#drawable/ic_action_tick"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:visibility="gone"/>
</RelativeLayout>
</LinearLayout>
ListView recycles views, so I was wrong code. My decisions
--- at ListFragment ---
private ListView lv;
getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapter, View view,
int position, long id) {
lv.setItemChecked(position, !lv.isItemChecked(position));
mAdapter.setCheckedItems(position, lv.isItemChecked(position));
return true;
}
});
--- at adapter --
private boolean [] checkedItems;
public void setCheckedItems(int position, boolean state){
checkedItems[position] = state;
notifyDataSetChanged();
}
public void unCheckAllItems(){
for (int i=0; i<checkedItems.length; i++){
checkedItems[i] = false;
}
}
private void SetDataView(ViewHolder holder, int position){
if (checkedItems[position]){
holder.operImgCheck.setVisibility(View.VISIBLE);
holder.operImg.setVisibility(View.INVISIBLE);
} else {
holder.operImgCheck.setVisibility(View.INVISIBLE);
holder.operImg.setVisibility(View.VISIBLE);
}
}
Main reason of problem: I should change and rebuild adapter but don't change items from ListView !!! Don't use access to ListView items by ListView for changing them.
Related
I have a Spinner that uses a custom adapter where the getDropDownView() is overridden. Each item in the custom drop-down view is made up of a TextView and a Button.
But, when I run my code, the spinner drop-down items display fine, but clicking them does nothing. The spinner drop-down remains open and spinner.onItemSelected() is not triggered.
drop_down_item.xml
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/dropdown_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:singleLine="true" />
<Button
android:id="#+id/dropdown_button"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_alignParentRight="true"
android:text="Remove"/>
</RelativeLayout>
Custom adapter code
public View getDropDownView(final int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.drop_down_item, parent, false);
TextView textView = (TextView) rowView.findViewById(R.id.dropdown_text);
textView.setText(mValues.get(position));
Button buttonView = (Button) rowView.findViewById(R.id.dropdown_button));
return rowView;
}
I create my spinner and adapter with this code:
spinner = (Spinner) findViewById(R.id.my_spinner);
MyAdapter adapter = new MyAdapter(getViewContext(), R.layout.spinner_item, values);
adapter.setDropDownViewResource(R.layout.drop_down_item);
spinner.setAdapter(adapter);
...
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
// Do something here - but this never runs
}
});
So I don't know why onItemSelected() is no longer called?
I was wondering if I need to put a click listener on the drop-down TextView which should in turn trigger onItemSelected() using maybe spinner.setSelection(pos)?
The Events is basically a interface that Activity implements to recieve the callBack by clicking on the LinearLayout of the DropDown View of Spinner.
public class MyArrayAdapter extends BaseAdapter {
String[] values;
int CustomResource;
Context context;
Events events;
public MyArrayAdapter(Context baseContext, int customspinnerview,
String[] stringArray, Events events) {
values = stringArray;
context = baseContext;
this.events = events;
CustomResource = customspinnerview;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return values.length;
}
#Override
public Object getItem(int position) {
if (position < values.length)
return values[position];
else {
return null;
}
}
#Override
public View getView(final int position, final View convertView,
ViewGroup parent) {
View rowView = convertView;
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (rowView == null) {
rowView = inflater.inflate(CustomResource, parent, false);
}
TextView textView = (TextView) rowView.findViewById(R.id.dropdown_text);
textView.setText(values[position]);
Button button = (Button) rowView.findViewById(R.id.Button_text);
return rowView;
}
#Override
public View getDropDownView(final int position, View convertView,
ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = convertView;`enter code here`
if (rowView == null) {
rowView = inflater.inflate(CustomResource, parent, false);
}
final LinearLayout parentRelative = (LinearLayout) rowView
.findViewById(R.id.parent);
final TextView textView = (TextView) rowView
.findViewById(R.id.dropdown_text);
textView.setText(values[position]);
Button button = (Button) rowView.findViewById(R.id.Button_text);
rowView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
events.onItemSelectedLister(
(AdapterView<?>) parentRelative.getParent(),
parentRelative, position, (long) 0);
}
});
// Button buttonView = (Button)
// rowView.findViewById(R.id.dropdown_button);
return rowView;
}
Events Inteface Its a interface that the Activity implement in order to recieve the callbacks from the Adapter.
import android.view.View;
import android.widget.AdapterView;
public interface Events {
public void onItemSelectedLister(AdapterView<?> parent, View view,
int position, long id);
}
Activity Implementation.
onItemSelected Implementation is the place where you can do your task.....
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import android.annotation.TargetApi;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Spinner;
import com.example.adapter.MyArrayAdapter;
public class MainActivity extends Activity implements Events {
Spinner spinner;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
spinner = (Spinner) findViewById(R.id.spinner);
spinner.setAdapter(new MyArrayAdapter(getBaseContext(),
R.layout.customspinnerview, getResources().getStringArray(
R.array.values), this));
}
#Override
public void onItemSelectedLister(AdapterView<?> parent, View view,
final int position, long id) {
//perform your Task.......
Method method;
try {
method = Spinner.class.getDeclaredMethod("onDetachedFromWindow");
method.setAccessible(true);
try {
method.invoke(spinner);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
spinner.post(new Runnable() {
#Override
public void run() {
spinner.setSelection(position);
spinner.setSelected(true);
((MyArrayAdapter) spinner.getAdapter()).notifyDataSetChanged();
}
});
}
}
Activity xml File for setContentView
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.customspinner.MainActivity" >
<Spinner
android:id="#+id/spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</Spinner>
</RelativeLayout>
Spinner View which is passed to Adapter as layout file.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#99000000"
android:id="#+id/parent"
android:orientation="horizontal">
<TextView
android:id="#+id/dropdown_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="#+id/Button_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="remove"/>
</LinearLayout>
the Code works perfectly fine :) .I have the code perfectly running.
Solution is to set android:focusable="false" in the layout for both the TextView and the Button.
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/dropdown_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:focusable="false"
android:singleLine="true" />
<Button
android:id="#+id/dropdown_button"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_alignParentRight="true"
android:focusable="false"
android:text="Remove"/>
</RelativeLayout>
Alternatively can also do this in the code:
textView.setFocusable(false);
buttonView.setFocusable(false);
Found the answer here. This works because the Spinner implementation only allows one focusable item in the view. That's why I wasn't able to select the item.
I am having a problem with a clickListener on my gridview. The LongClickListener works without issue. But I cannot seem to get any response from the click Listener.
My code is below.
Im confused as to why the long click works but not the normal click,
Any pointers would be appreciated
Thanks
final GridView gridView = (GridView) findViewById(R.id.grid_view);
gridView.setNumColumns(numOfColumns);
gridView.getLayoutParams().width = (CELL_WIDTH * numOfColumns);
gridView.getLayoutParams().height = (CELL_WIDTH * numOfRows);
....
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Log.d("ABCD", "Position Single Click is " + position);
// Ideally in here I want to put to open a soft keyboard for the user to enter a value
// InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
// imm.showSoftInput(gridView, InputMethodManager.SHOW_IMPLICIT);
}
});
gridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Log.d("ABCD", "Position Long Click is " + position);
return true;
}
});
grid_view is
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal">
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"/>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/my_grid_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"/> <<--- I WANT THIS TO GET THE CLICK
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"/>
</LinearLayout>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/listId"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp" />
</LinearLayout>
GridCell in the grid view is
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:padding="0dp" android:layout_margin="0dp"
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"
>
<TextView
android:id="#+id/grid_item_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="1dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:textSize="10px"
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"
>
</TextView>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/grid_item_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#+id/celllabel"
android:background="#android:color/transparent"
android:paddingLeft="5dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:layout_margin="0dp"
android:focusable="false"
android:focusableInTouchMode="false"
android:clickable="false"
android:cursorVisible="false">
</EditText>
</RelativeLayout>
The adapter class has a getView and is as below
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
MyObject obj = myObjects.get(position);
if (convertView == null) {
gridView = inflater.inflate(R.layout.grid_cell, null);
String textColour = "#000000";
TextView textView = (TextView) gridView.findViewById(R.id.grid_item_label);
textView.setText(Html.fromHtml(String.format("<font color='%s'>%s</font>", textColour, obj.getValue())));
TextView superScriptTv = (TextView) gridView.findViewById(R.id.grid_item_number);
if (obj.getNumber() > 0) {
superScriptTv.setText(Html.fromHtml(String.format("<font>%s</font>", cell.getNumber())));
}
} else {
gridView = convertView;
}
gridView.setBackgroundColor(obj.getBackgroundColour());
return gridView;
}
EDIT
Really banging my head against a wall here now :)
Im updating the code sample so have more data. Ive noticed that in my adapter if I do not set the text on the textview with ID = R.id.grid_item_number then it works. As soon as I set text on it then I lose the click listener.
The linked question/answer doesnt help from what I can see. Can anyone help with my stupidity?
EDIT
Adapter code has been added.
Thanks in advance.
The problem is with the EditText inside the row_cell. When you click on the item, it takes focus and prevents the whole item to be clickable again. As you noticed, only long click works.
Here you have a similar problem.
To resolve that issue I would move your OnItemClickListeners from the Activity / Fragment to your GridViewAdapter, so instead of:
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Log.d("ABCD", "Position Single Click is " + position);
// Ideally in here I want to put to open a soft keyboard for the user to enter a value
// InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
// imm.showSoftInput(gridView, InputMethodManager.SHOW_IMPLICIT);
}
});
gridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Log.d("ABCD", "Position Long Click is " + position);
return true;
}
});
I would do something like that:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridViewItem;
if (convertView == null) {
gridViewItem = new View(mContext);
gridViewItem = layoutInflater.inflate(R.layout.grid_cell, null);
TextView textView = (TextView)gridViewItem.findViewById(R.id.grid_item_number);
textView.setText(mValues[position]);
EditText editText = (EditText)gridViewItem.findViewById(R.id.grid_item_label);
} else {
gridViewItem = (View) convertView;
}
gridViewItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.e("GRID", "onClick: " );
}
});
gridViewItem.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Log.e("GRID", "onLongClick: " );
return true;
}
});
return gridViewItem;
}
It will prevent this strange behaviour you are struggling right now.
For the sake of that example please find my code below:
MainActivity layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="io.github.mmbs.gridviewcheck.MainActivity"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="0dp">
<GridView
android:id="#+id/gridView"
android:numColumns="auto_fit"
android:columnWidth="100dp"
android:stretchMode="columnWidth"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true">
</GridView>
</android.support.constraint.ConstraintLayout>
Grid cell layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:padding="8dp">
<TextView
android:id="#+id/grid_item_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="0dp"
android:paddingLeft="1dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
android:textSize="20sp"
android:text="TEXTVIEW">
</TextView>
<EditText
android:id="#+id/grid_item_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:background="#android:color/transparent"
android:cursorVisible="false"
android:layout_below="#id/grid_item_number"
android:text="EDITTEXT">
</EditText>
</RelativeLayout>
Please take into consideration, that I removed all layouts attributes responsible for focusability.
MyGridAdapter:
public class MyGridViewAdapter extends BaseAdapter {
private Context mContext;
private final String[] mValues;
public MyGridViewAdapter(String[] values, Context context) {
mValues = values;
mContext = context;
}
#Override
public int getCount() {
return mValues.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridViewItem;
if (convertView == null) {
gridViewItem = new View(mContext);
gridViewItem = layoutInflater.inflate(R.layout.grid_cell, null);
TextView textView = (TextView)gridViewItem.findViewById(R.id.grid_item_number);
textView.setText(mValues[position]);
EditText editText = (EditText)gridViewItem.findViewById(R.id.grid_item_label);
} else {
gridViewItem = (View) convertView;
}
gridViewItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.e("GRID", "onClick: " );
}
});
gridViewItem.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Log.e("GRID", "onLongClick: " );
return true;
}
});
return gridViewItem;
}
}
If you like, I can also share this code on github, so you will have a fully operational example.
The second option is to leave your implementation as it is and do hacks when the EditText is focusable and enabled. Here you have related topics:
Android: Force EditText to remove focus?
ListView items focus behaviour
Focusable EditText inside ListView
Android: Force EditText to remove focus?
What is more, please take into account that answer:
Do not use clickable objects in the grid. In that case Android cannot handle the click event of GridView.
Instead, use something to show a similar user interface view. Then handle that object's click actions.
Don't: put Button in the GridView to perform some click actions.
Do: put an ImageView instead of ImageButton and handle ImageView's click events.
Edit: Please find a link to the project on my Github.
use gridView.setOnItemClickListener(.....) and to your root view add below line
android:descendantFocusability="blocksDescendants"
The ViewGroup will block its descendants from receiving focus.
Try to add
android:focusable="false"
android:focusableInTouchMode="false"
in your GridCell -->TextView
Try using recycler view if you are comfortable with it.
It doesn't gives any such problem in addition to this it has its own advantages.
Follow the link for complete explanation.
If you have any focus-able view in you your row layout, then the onItemClickListener will not be called. For this problem you need to customize your getView code and set onClickListener() on convertView and pass the callback to activity using the Interface. I have updated the code of your Adapter as below. Now you need to implement the GridViewItemClickListener on your activity and pass the instance while creating the Adapter instance.
public class MyGridViewAdapter extends BaseAdapter {
private Context mContext;
private final String[] mValues;
private GridViewItemClickListener mListener;
public MyGridViewAdapter(String[] values, Context context,GridViewItemClickListener listener ) {
mValues = values;
mContext = context;
mListener = listener;
}
#Override
public int getCount() {
return mValues.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridViewItem;
if (convertView == null) {
gridViewItem = new View(mContext);
gridViewItem = layoutInflater.inflate(R.layout.grid_cell, null);
TextView textView = (TextView)gridViewItem.findViewById(R.id.grid_item_number);
textView.setText(mValues[position]);
EditText editText = (EditText)gridViewItem.findViewById(R.id.grid_item_label);
} else {
gridViewItem = (View) convertView;
}
gridViewItem.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Log.e("GRID", "onLongClick: " );
return true;
}
});
//Add an OnclickListener here
gridViewItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mListener != null){
mListener.onGridItemClick(v, position);
}
}
});
return gridViewItem;
}
public interface GridViewItemClickListener{
void onGridItemClick(View v, int index);
}
}
You can user click listeners inside the adapter it will work ,it should work like this
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
MyObject obj = myObjects.get(position);
if (convertView == null) {
gridView = inflater.inflate(R.layout.grid_cell, null);
String textColour = "#000000";
TextView textView = (TextView) gridView.findViewById(R.id.grid_item_label);
textView.setText(Html.fromHtml(String.format("<font color='%s'>%s</font>", textColour, obj.getValue())));
TextView superScriptTv = (TextView) gridView.findViewById(R.id.grid_item_number);
superScriptTv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// here you can use every item of grid acc to position
}
});
if (obj.getNumber() > 0) {
superScriptTv.setText(Html.fromHtml(String.format("<font>%s</font>", cell.getNumber())));
}
} else {
gridView = convertView;
}
gridView.setBackgroundColor(obj.getBackgroundColour());
return gridView;
}
how can the click listener work if you set the root element in GridCell.java as non-clickable ?
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:padding="0dp" android:layout_margin="0dp"
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"
Remove those lines from each of your components in the GridCell.java
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"
I don't think this is required for GridView, but sometimes for RecyclerView it is required to have android:clickable="true" on the root componenent of GridCell.java
I have this code for a ListView on a Fragment. My goal is to select multiple items on the list and to highlight them. My code does highlight the item, but when I scroll the list, more items are highlighted and when I scroll up again, more items are highlighted again and it goes crazy over and over highlighting and changing to default state, whenever I scroll the list. I don't know why it happens.
NOTE: I've tried CHOICE_MODE_SINGLE, CHOICE_MODE_MULTIPLE, CHOICE_MODE_MULTIPLE_MODAL and I also tried MultiChoiceModeListener but my App should be working on API above 9. So that's not a solution for me.
My Fragment (Don't want to use ListFragment)
public class InventarioFragment extends Fragment {
ListView listInventario;
public static InventarioFragment newInstance()
{
return new InventarioFragment();
}
#Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_inventario_list,container,false);
listInventario = (ListView) view.findViewById(R.id.list_inv);
listInventario.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
v.setBackgroundColor(Color.BLUE);
}
});
return view;
}
}
This is the XML of my Fragment (Notice that I am using an array for the items on the list).
My Fragment XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:orientation="horizontal"
android:weightSum="1">
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5">
<ImageButton
android:id="#+id/ib_cancel_inventario"
android:layout_width="70dp"
android:layout_height="70dp"
android:background="#drawable/btn_cancel"
android:layout_centerInParent="true"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5">
<ImageButton
android:id="#+id/ib_submit_inventario"
android:layout_width="70dp"
android:layout_height="70dp"
android:background="#drawable/btn_ok"
android:layout_centerInParent="true"/>
</RelativeLayout>
</LinearLayout>
<TextView
android:layout_width="fill_parent"
android:layout_height="20dp"
android:background="#DF0101"/>
<ListView
android:id="#+id/list_inv"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:entries="#array/list_inventario"
>
</ListView>
</LinearLayout>
</LinearLayout>
I'm not using and Adapter because I set the list items from a default array.
Sorry for my english, and I hope you can help me. (I can't upload images because I don't have enough reputation yet).
You can use the following adapter to achieve your goal.
Remove android:entries="#array/list_inventario" in your Listview and add choicemode as follows in your XML.
<ListView
android:id="#+id/list_inv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="multipleChoice">
</ListView>
InventarioFragment
public class InventarioFragment extends Fragment {
private Activity activity;
private ListView listInventario;
private String[] inventatioItems;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.activity=activity;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_inventario_list,container,false);
listInventario = (ListView) view.findViewById(R.id.list_inv);
inventatioItems=getResources().getStringArray(R.array.list_inventario);
InverntarioAdapter adapter=new InverntarioAdapter(activity);
listInventario.setAdapter(adapter);
listInventario.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View v, int pos, long arg3) {
listInventario.setItemChecked(pos, true);;
}
});
return view;
}
private class InverntarioAdapter extends BaseAdapter{
private LayoutInflater inflator;
public InverntarioAdapter(Context context){
inflator=(LayoutInflater) context.getSystemService(activity.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return inventatioItems.length;
}
#Override
public Object getItem(int position) {
return inventatioItems[position];
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
TextView tv;
if(convertView == null){
convertView=inflator.inflate(android.R.layout.simple_list_item_1, null);
convertView.setBackground(getResources().getDrawable(R.drawable.selector_background));
}
tv=(TextView) convertView.findViewById(android.R.id.text1);
tv.setText(inventatioItems[position]);
if(listInventario.isItemChecked(position)){
//background Color of selected items
convertView.setBackgroundColor(Color.BLUE);
}
else{
convertView.setBackgroundColor(Color.WHITE);
}
return convertView;
}
}
}
I'm trying to dynamically add click listeners to a custom array adapter for a list view. The idea is that I have list objects with text on them (eg "Group 1", "Group 2" etc.), and then when I click on the object a drop down appears ("expandable_section") with two buttons on it ("Members" and "Settings"). When I click on the members button I want to be able to access the specific text on the original group object. For example, clicking "Members" under the "Group 1" tab would give me access to the text "Group 1."
Here is my XML layout for the custom adapter object:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<!-- The list element for GROUP SETTINGS tab sub-section -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:paddingBottom="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp" >
<ImageView
android:id="#+id/activeIcon"
android:layout_width="34dp"
android:layout_height="34dp"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="5dp"
android:layout_marginLeft="-7dp"
android:layout_marginTop="5dp"
android:gravity="center_vertical" />
<ImageView
android:id="#+id/modeIcon"
android:layout_width="34dp"
android:layout_height="34dp"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="5dp"
android:layout_marginRight="15dp"
android:layout_marginTop="5dp"
android:gravity="center_vertical" />
<TextView
android:id="#+id/groupName"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:gravity="center_vertical"
android:textColor="#000000"
android:textSize="22dp" />
</LinearLayout>
<!-- SPECIAL HIDDEN TOOLBAR FOR DEMO LIBRARY LIST MODES TODO: Change to whitelist/blacklist GROUP TAB! -->
<!-- EXPANDABLEFOR GROUP-SETTINGS FRAGMENT -->
<LinearLayout
android:id="#+id/expandableSubsection"
android:layout_width="fill_parent"
android:layout_height="90dp"
android:layout_marginBottom="-100dp"
android:background="#dddddd"
android:gravity="center"
android:visibility="gone" >
<Button
android:id="#+id/groupMembersButton"
android:layout_width="150dp"
android:layout_height="40dp"
android:layout_marginRight="20dp"
android:focusable="false"
android:focusableInTouchMode="false"
android:text="Members" />
<Button
android:id="#+id/groupSettingsButton"
android:layout_width="150dp"
android:layout_height="40dp"
android:layout_marginRight="20dp"
android:focusable="false"
android:focusableInTouchMode="false"
android:text="Settings" />
</LinearLayout>
</LinearLayout>
Here is the custom adapter code:
package com.sapphire.view;
import java.util.List;
import com.sapphire.model.Mode;
import com.sapphire.view.ModeAdapter.ModeHolder;
import android.R;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class GroupAdapter extends ArrayAdapter<String>{
private Context context;
private int resource;
public GroupAdapter(Context context, int resource, int textViewResourceId,
List<String> objects) {
super(context, resource, textViewResourceId, objects);
this.context = context;
this.resource = resource;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi =(LayoutInflater)this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(this.resource, null);
}
Button but= (Button) v.findViewById(R.id.button1);
if( but!=null){
but.setId(position);
but.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("customgroup clicked !");
}
});
}
return v;
}
}
and here is where the custom adapter is actually used to display the list of groups:
public void displayGroupsOptions(){
final ListView lView = (ListView) thisView.findViewById(R.id.GroupsView);
final ArrayList<String> groupNameList = new ArrayList<String>();
lView.setAdapter(null); //empty the list view
//Get all groups from database table
System.out.println("customgroup about to get all groups from database");
List<Group> allGroups = db.getAllGroups();
if(!allGroups.isEmpty()){
for (Group g: allGroups){
groupNameList.add(g.getName());
}
//Convert group arraylist to group array for use in adapter for list view
String[] groupNameArray = (String[]) groupNameList.toArray(new String[0]);
GroupAdapter adapter = new GroupAdapter(getActivity().getApplicationContext(), R.layout.group_list_row, R.id.groupName, groupNameList);
lView.setAdapter(adapter);
// Creating an item click listener, to open/close the mode options toolbar for each item
lView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
View groupOptions = view.findViewById(R.id.expandableSubsection);
// Creating the expand animation for the item
ExpandAnimation ea = new ExpandAnimation(groupOptions, ANIMATION_DELAY);
// Start the animation on the toolbar
groupOptions.startAnimation(ea);
}
});
}
}
Any help would be appreciated! I just can't figure out for the life of me how to get the proper text to show up on the tab (right now it's blank) and how to make the onclick listener actually attach and work. Thanks!
You can set tag for your Button in getView method to pass the position argument, and get the tag in th ClickListener.
btn.setTag(position);
#Override
public void onClick(View v) {
int position = (Integer)v.getTag();
}
Even you can set other view in the same adapter item as a tag and pass it to the listener.
use this piece of code where you used getView in adapter class
public class ViewHolder {
Button b;
}
and also this
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.xyz,
parent, false);
holder.b = (Button) convertView.findViewById(R.id.button1);
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}
Item it = item.get(position);
holder.b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("customgroup clicked !");
}
});
return convertView;
}
I have to admit I'm a newbie in Android and my ExpandableListView is causing me a lot of trouble. I've figured it's the adapter which isn't working properly. I'm using a class which extends BaseExpandableListAdapter.
From logging I know this much:
The list I hand over to the constructor of the adapter is filled correctly. getGroupCount returns 1 (yes, there's only 1 group) and getChildrenCount returns 18 (as expected). The method getGroupView is called (Log.d...) but getChildView isn't - not when logging at the very start of this method and not while debugging (didn't reach the breaking point).
There are no error messages - it's just the ExpandableListView quietly not expanding.
Any idea what went wrong? Do I need a ViewHolder or is it not inflating the group properly? I'm totally lost...
Part of my code:
package de.cimitery.android.cimitery;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.CheckBox;
import android.widget.TextView;
public class ExpandableAdapter extends BaseExpandableListAdapter {
private List<GroupCat> catList;
private Context ctx;
NewGraveActivity app;
public ExpandableAdapter(List<GroupCat> catList, Context ctx, NewGraveActivity app) {
this.catList = catList;
this.ctx = ctx;
this.app = app;
}
#Override
public Object getGroup(int groupPosition) {
return catList.get(groupPosition);
}
#Override
public int getGroupCount() {
Log.d("ExAdapter getGroupCount", "" + catList.size());
return catList.size();
}
#Override
public long getGroupId(int groupPosition) {
return catList.get(groupPosition).hashCode();
}
#Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
Log.d("ExAdapter getGroupView", "Start");
View v = convertView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater)ctx.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.group_layout, parent, false);
}
TextView groupName = (TextView) v.findViewById(R.id.groupName);
GroupCat cat = catList.get(groupPosition);
groupName.setText(cat.getGroupName());
return v;
}
#Override
public Object getChild(int groupPosition, int childPosition) {
return catList.get(groupPosition).getItemList().get(childPosition);
}
#Override
public long getChildId(int groupPosition, int childPosition) {
return catList.get(groupPosition).getItemList().get(childPosition).hashCode();
}
#Override
public View getChildView(final int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
Log.d("ExAdapter getChildView", "Start");
View v = convertView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater)ctx.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.item_layout, parent, false);
}
CheckBox itemCheck = (CheckBox) v.findViewById(R.id.itemCheck);
TextView itemName = (TextView) v.findViewById(R.id.itemName);
if (app.selected.containsKey((groupPosition))==true)
itemCheck.setChecked(true);
else
itemCheck.setChecked(false);
Category child = catList.get(groupPosition).getItemList().get(childPosition);
Log.d("ExAdapter getChildView", child.getName());
itemName.setText(child.getName());
itemCheck.setOnCheckedChangeListener(new CheckListener(childPosition, app));
return v;
}
#Override
public int getChildrenCount(int groupPosition) {
int size = catList.get(groupPosition).getItemList().size();
System.out.println("number of children for group ["+groupPosition+"] is ["+size+"]");
return size;
}
}
group-layout:
<?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" >
<TextView
android:id="#+id/groupName"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="15dip" />
<TextView
android:id="#+id/groupDescr"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="8dip" />
</LinearLayout>
item layout:
<?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" >
<CheckBox
android:id="#+id/itemCheck"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:id="#+id/itemName"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
layout with exp. listview:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ExpandableListView
android:id="#+id/expandableListView1"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_marginTop="10dp" >
</ExpandableListView>
<Button
android:id="#+id/buttonNewGrave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/buttonNewGrave" />
</LinearLayout>
</ScrollView>
First thing:
You should not use any scrolling component like expandable list, inside a scroll view here's why Listview inside ScrollView is not scrolling on Android.
Second thing:
Height of ExpandableListView should be match_parent. In your case you have taken Expandable ListView within Linear Layout whose height is wrap_content, change it to match_parent.
I had faced the same problem and I resolved that problem by following the same.