GestureDetector detect DoubleClick on GridView items - java

Note: This specific problem is solved, but there are serious follow-up problems. Have a look at GestureDetector - Detect double click in GridView item's although returning false in onTouchEvent()
I want to detect double clicks on distinct items in a GridView of images.
Therefore I assigned a separate OnTouchListener to each item-imageView in the getView() method of the adapter. The gestureDetector is a member variable of the adapter-class.
private GestureDetectorCompat gestureDetector;
public ImageGridViewAdapter(Context c, ArrayList<UriWrapper> startUpImages) {
mContext = c;
uriManager = new UriManager(startUpImages);
gestureDetector = new GestureDetectorCompat(mContext, new SingleTapConfirm());
}
public View getView(final int position, View recycled, ViewGroup parent) {
ViewHolder holder;
if (recycled == null) {
..... find items by id
} else{
holder = (ViewHolder) recycled.getTag();
}
// Set listener to item image
holder.image.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// Always returns false, the gestureDetector does not detect anything
boolean ret = gestureDetector.onTouchEvent(event);
// At least the onTouch-callback gets called with the correct position
Log.e(TAG, "onTouch returned " + ret + " at position " + position);
return true;
}
});
// Use glide library to load images into the image views
Glide.with(mContext)....into(holder.image);
return recycled;
}
private class SingleTapConfirm extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onSingleTapConfirmed(MotionEvent event) {
Log.e(TAG, "onSingleTapConfirmed"); // never called..
return true;
}
#Override
public boolean onDoubleTap(MotionEvent e) {
Log.e(TAG, "onDoubleTap"); // never called..
return super.onDoubleTap(e);
}
}
The OnTouchListener's work and get called with the correct position.
However, no matter what I am doing, the methods of the GestureDetector are never called. What seems to be the issue with this code?
Update: The onTouch-callback needs to return true, now at least the GestureDetector works. However, returning true breaks the rest of the functionality, since I have a long-click-selection-mode and a global OnTouchListener for my GridView.
Second Update:
Merging the item-specific OnTouchListener and the global OnTouchListener did not work properly. (swipe gestures only recognized on certain items)
I hope that I can work around these two problems by creating a custom View extending ImageView and assigning the item-specific OnTouchListener there.

private GestureDetectorCompat gestureDetector;
// in your adapter constructor
gestureDetector = new GestureDetector(context, new SingleTapConfirm());
public View getView(final int position, View recycled, ViewGroup parent) {
ViewHolder holder;
if (recycled == null) {
.....find items by id
} else {
holder = (ViewHolder) recycled.getTag();
}
// Set listener to item image
holder.image.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// Use lazy initialization for the gestureDetector
gestureDetector.onTouchEvent(event);
// At least the onTouch-callback gets called with the correct position
return true;
}
});
// Use glide library to load images into the image views
Glide.with(mContext)....into(holder.image);
return recycled;
}
private class SingleTapConfirm extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onSingleTapConfirmed(MotionEvent event) {
Log.e(TAG, "onSingleTapConfirmed"); // never called..
return true;
}
#Override
public boolean onDoubleTap(MotionEvent e) {
Log.e(TAG, "onDoubleTap"); // never called..
return super.onDoubleTap(e);
}
}
Update:
#Override
public boolean onTouch(View v, MotionEvent event) {anything
gestureDetector.onTouchEvent(event);
return true;
}

Related

Why Scrolling Up and Down In GridView Changes Registries in SharedPreferences?

My question is similar to this and this. But I cannot apply the solutions in those questions to my case.
My case is as follows: I have GridView consists of just CheckBoxes. I set setOnCheckedChangeListener in my Adapter's getView method, so that as soon as the status of CheckBox is changed, current status of the CheckBox is registered to SharedPreferences. And in getView also, I'm reading the current status of the CheckBox and set its status as so. Yet scrolling down and back up makes the checked boxes unchecked.
How can I solve this problem?
Here is my custom adapter:
public class KnowledgeItemAdapter extends ArrayAdapter<KnowledgeItem> {
private String currentKey;
public KnowledgeItemAdapter(Activity context, ArrayList<KnowledgeItem> knowledgeItems){
super(context, 0, knowledgeItems);
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View knowledgeItemView = convertView;
if (knowledgeItemView == null){
knowledgeItemView = LayoutInflater.from(getContext()).inflate(R.layout.item_knowledge, parent, false);
}
knowledgeItemView.setClickable(true);
knowledgeItemView.setFocusable(true);
knowledgeItemView.setBackgroundResource(android.R.drawable.menuitem_background);
final KnowledgeItem currentKnowledgeItem = getItem(position);
final CheckBox knowledgeCheckBox = (CheckBox) knowledgeItemView.findViewById(R.id.item_knowledge_check_box);
final SharedPreferences prefs = getContext().getSharedPreferences("com.example.myapplication", Context.MODE_PRIVATE);
currentKey = currentKnowledgeItem.getPrefsKey();
Boolean status = prefs.getBoolean(currentKey, false);
knowledgeCheckBox.setText(currentKnowledgeItem.getText());
knowledgeCheckBox.setChecked(status);
knowledgeCheckBox.setOnCheckedChangeListener( //this);
new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
prefs.edit().putBoolean(currentKnowledgeItem.getPrefsKey(), isChecked).apply();
}
}
);
return knowledgeItemView;
}
}
OK I've figured out the answer finally. I've changed the onCheckedChangeListener with onClickListener.
knowledgeItemView.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.e("On Click Listener", currentKey);
if(knowledgeCheckBox.isChecked()){
knowledgeCheckBox.setChecked(false);
prefs.edit().putBoolean(currentKey, false).apply();
} else {
knowledgeCheckBox.setChecked(true);
prefs.edit().putBoolean(currentKey, true).apply();
}
}
}
);
And do not forget to change the clickable xml attribution of the checkbox to false. Otherwise click events of checkbox and view conflict.

How to create an algorithm to set a multi checked items in a RecyclerView

I need to create an algorithm that set a checked item in a RecyclerView. I have a RecyclerView made with boxes with a TextView and an ImageView, I use the TextView to show nameItems from a List and the ImageView to show a check once the user clicks on it.
What I want to do is that every time the user clicks on an item the check appears and every time he clicks on a checked Item the check disappears.
I create an algorithm which uses a boolean variable (isChecked) set to false, every time user clicks on an item the variable is set to true and vice versa.
In this case, a user has to click on the next item of the list two times to let show the check.
How can I do it?
Thank you so much in advance
Here is my Adapter's class:
public class RecyclerTypeListViewAdapter extends RecyclerView.Adapter<RecyclerTypeListViewAdapter.TypeViewHolder> {
List<TipologiaEvento> eventType;
private boolean isChecked = false;
public RecyclerTypeListViewAdapter (List<TipologiaEvento> typeList){
this.eventType = typeList;
}
public static class TypeViewHolder extends RecyclerView.ViewHolder {
LinearLayout linearLayout;
TextView typeName;
ImageView check_icon;
TypeViewHolder (View view){
super(view);
linearLayout = view.findViewById(R.id.type_listed_linear_layout);
typeName = view.findViewById(R.id.event_type_text_view);
check_icon = view.findViewById(R.id.event_type_checked_icon);
}
}
#Override
public TypeViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.cell_type_listed, viewGroup, false);
TypeViewHolder tvh = new TypeViewHolder(view);
return tvh;
}
#Override
public void onBindViewHolder(final TypeViewHolder typeViewHolder, int position) {
typeViewHolder.typeName.setText(eventType.get(position).getDescrizione());
typeViewHolder.linearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(isChecked == false) {
typeViewHolder.check_icon.setVisibility(View.VISIBLE);
Toast.makeText(v.getContext(), "Aggiunto: " + typeViewHolder.typeName.getText().toString(), Toast.LENGTH_SHORT).show();
isChecked = true;
} else {
typeViewHolder.check_icon.setVisibility(View.INVISIBLE);
Toast.makeText(v.getContext(), "Rimosso: " + typeViewHolder.typeName.getText().toString(), Toast.LENGTH_SHORT).show();
isChecked = false;
}
}
});
}
}
Just take one boolean value in the model class of your list. and in bindviewholder.
just put the condition like below
if(list.get(position).ischecked()) {
action of checked
} else {
action of unchecked
}
and on the click of the item you just have to change the flag of object and notify the adapter.

How can I get baseadapter to start at a different position than 0 or 1

I have a baseadapter and it always starts at position 0 or 1 at the very top but is there someway that I can make my Baseadapter start at position 5? I been searching on here and have not found information on that, this is my baseadapter and yes it is connected to a listview but I know that the position that the listview starts is controlled by the BaseAdapter
public class myadapter extends BaseAdapter {
Context context;
LayoutInflater myiflater;
public myadapter() {
myiflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return 20;
}
#Override
public Object getItem(int position) {
return 5;
}
#Override
public long getItemId(int position) {
return 5;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// all of this is already correct
}
catch (Exception e) {
e.printStackTrace();
}
return convertView;
}
return convertView;
}
}
As you can see I have tried to set the starting position at 5 but it will not work.
Do this with your ListView:
mListView.setSelection(5);
setSelection() sets the current position of the ListView via the adapter. The way you are trying doesn't make sense. For the getItem() method you are supposed to return the object, not a numerical value for position. Let me know if that works

Passing MotionEvents from RecyclerView.OnItemTouchListener to GestureDetectorCompat

I have a Fragment that implemets RecyclerView.OnItemTouchListener. How do I pass click and long-click motion events only from the RecyclerView to the GestureDetectorCompat. That is I mean I only want to handle clicks and long-clicks, rest of the events should be handled by the RecyclerView as it would happen normally. How can I set this up?
public class MyFragment extends Fragment implements RecyclerView.OnItemTouchListener,
GestureDetector.OnGestureListener {
protected RecyclerView recyclerView;
protected RecyclerView.Adapter adapter;
protected LinearLayoutManager layoutManager;
private GestureDetectorCompat detector;
public MyFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.myfrag, container, false);
recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview);
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.addOnItemTouchListener(this);
adapter = new MyAdapter(myData));
recyclerView.setAdapter(adapter);
return rootView;
}
#Override
public boolean onDown(MotionEvent e) {
return false;
}
#Override
public void onShowPress(MotionEvent e) {
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
#Override
public void onLongPress(MotionEvent e) {
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
#Override
public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent event) {
return false;
}
#Override
public void onTouchEvent(RecyclerView recyclerView, MotionEvent event) {
}
}
You have to initialize GestureDetectorCompat in onCreateView() method:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.myfrag, container, false);
detector = new GestureDetectorCompat(getActivity(), new RecyclerViewOnGestureListener());
recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview);
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.addOnItemTouchListener(this);
adapter = new MyAdapter(myData));
recyclerView.setAdapter(adapter);
return rootView;
}
RecyclerViewOnGestureListener is your own inner class extending SimpleOnGestureListener (that provides empty implementation of OnGestureListener methods)
private class RecyclerViewOnGestureListener extends SimpleOnGestureListener {
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
View view = recyclerView.findChildViewUnder(e.getX(), e.getY());
int position = recyclerView.getChildPosition(view);
// handle single tap
return super.onSingleTapConfirmed(e);
}
public void onLongPress(MotionEvent e) {
View view = recyclerView.findChildViewUnder(e.getX(), e.getY());
int position = recyclerView.getChildPosition(view);
// handle long press
super.onLongPress(e);
}
}
Now look at line (from onCreateView() method):
recyclerView.addOnItemTouchListener(this);
In our case 'this' is OnItemTouchListener containing two methods we need to implement:
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
detector.onTouchEvent(e);
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
Here is an explanation what these methods mean:
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.OnItemTouchListener.html
It's all you need to handle single tap and long press events from RecyclerView.
I might be late but for visual feedback add this to your list_item layout
android:background="#drawable/tranparent_selector"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
Also, onSingleTapUp can be used instead of onSingleTapConfirmed as tapping is usually fast. So if you tap fast to other items you wont get it working.
For fast tapping I prefer onSingleTapUp

Disabled click Item of ListView

I am using SimpleAdapter to bind ListView. Now, When User Clicks on Item, I want to disable that time from Click() event. I found some tutorial for isEnabled() but I am not understanding that how to use it ?
Please help me to solve this problem. I am using Custom ListView.
This below code disables ListView.
SimpleAdapter adapter = new SimpleAdapter(this, arrlist,
R.layout.topicwisequestion, new String[] { "option" },
new int[] { R.id.option }) {
public boolean areAllItemsEnabled() {
return ignoreDisabled;
}
public boolean isEnabled(int position) {
if (areAllItemsEnabled()) {
return true;
}
return false;
}
};
lvTWOptions.setAdapter(adapter);
lvTWOptions.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Code...
}
});
Do it like this.
define a boolean variable
boolean ignoreDisabled = false;
Then in areAllItemsEnabled:
public boolean areAllItemsEnabled() {
return ignoreDisabled;
}
and then at the beginning of isEnabled:
public boolean isEnabled(int position) {
if (areAllItemsEnabled()) {
return true;
}
}
or else second way is define following code in your adapter class
private int mLastClicked;
public void setLastClicked(int lastClicked){
mLastClicked = lastClicked;
}
and then
public boolean areAllItemsEnabled() {
return false;
}
public boolean isEnabled(int position) {
// return false if position == position you want to disable
}
add position of your last click event with isEnabled method.
For further reference you can check this link
Hope this is gonna help you.
Do it like this.
ListAdapter adapter = new SimpleCursorAdapter(MyList, Layout, c,
new String[] { "Name", "Score" }, to)
{
public boolean areAllItemsEnabled()
{
return false;
}
public boolean isEnabled(int position)
{
return false;
}
};
I have used below code and solved my problem. It is very simple and easy. No need to use any extra codes.
What I have done is I have put condition to check position and then based on it, I disabled Clicked Item.
My Code :
int pos;
SimpleAdapter adapter = new SimpleAdapter(this, arrlist, R.layout.topicwisequestion, new String[] { "option" }, new int[] { R.id.option }) {
public boolean isEnabled(int position) {
if (position != 0) {
if (position == pos) {
return false;
} else {
return true;
}
} else {
return true;
}
}
};
lvTWOptions.setAdapter(adapter);
lvTWOptions.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
pos = position;
}
});
You'll have to extend the simple adapter, maintain a list of indexes that have been clicked on, implement isEnabled(int) and check if the integer passed is in your list. You can then selectively disable list items after they've been clicked.
Adding some code:
public class MySimpleAdapter extends SimpleAdapter {
private List<Integer> mDisabledRows;
public MySimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) {
super(context, data, resource, from, to);
mDisabledRows = new ArrayList<Integer>();
}
public void disableRow(int index) {
mDisableRows.clear();
mDisabledRows.add(index);
}
#Override
public boolean isEnabled(int index) {
return !mDisabledRows.contains(index);
}
}
From your onItemClick method you would call adapter.disableRow(position)
I read that as you want to disable a row once it's clicked. If you only want to disable the last clicked row you could just store the last index clicked.

Categories

Resources