I have viewpager with tablayout, Inside ViewPager I'hv a Fragment which are creating a RecyclerView,
In RecyclerVIew roe item I have a created a Horizontal ScrollView, for getting swipe left/right motion,
Basically I want to swipe left the row only 75% of the screen and then show the swipeable view,
I was also used ItemTouchHelper of RecyclerView but it swiping the complete row (100% swipe the row),
I know there are libraries available on google for creating swipe left and right gesture,
But all these not working properly because When we swipe on recyclerview item, sometimes touches goes to ViewPager therefore page get swiped I don't want that,
So that I using HorizontalScrollView, but the problem is that I can't detect swipe direction inside touch listner of HorizontalScrollView,
Actually we want to auto scroll after some amount if scroll on HorizontalScrollView,
Below is code which I have tried.
holder.scrollContainer.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int scroll1st = 0;
int scroll2nd = 0;
if(event.getAction() == MotionEvent.ACTION_DOWN){
scroll1st = holder.scrollContainer.getScrollX();
Log.d("SCROLL", "Scroll down callded: amt is: " + holder.scrollContainer.getScrollX() );
mDataSet.get(position).isFirstSwipe = false;
} else if (event.getAction() == MotionEvent.ACTION_UP){
scroll2nd = holder.scrollContainer.getScrollX();
Log.d("SCROLL", "Scroll up callded: amt is: " + holder.scrollContainer.getScrollX() );
}
Log.d("SCROLL", "max scroll amount: " + holder.scrollContainer.getMaxScrollAmount());
if(event.getAction() == MotionEvent.ACTION_UP){
if(scroll2nd > scroll1st && (scroll2nd - scroll1st ) > 50 ){
holder.scrollContainer.postDelayed(new Runnable() {
public void run() {
holder.scrollContainer.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}
}, 100L);
mDataSet.get(position).isFirstSwipe = true;
} else if (scroll1st > scroll2nd && (scroll1st - scroll2nd ) > 50){
holder.scrollContainer.postDelayed(new Runnable() {
public void run() {
holder.scrollContainer.fullScroll(HorizontalScrollView.FOCUS_LEFT);
}
}, 100L);
mDataSet.get(position).isFirstSwipe = true;
}
}
v.onTouchEvent(event);
return true;
}
});
Basically ACTION.DOWN event is not called every time so can't not derived swipe direction
Make sure the RecyclerView is passing events to the scroll container.
add onTouchListener to RecyclerView and pass events to your ScrollView.
Related
Really not sure if I understand this or maybe going about it wrong. I have a gridlayout (cells are textview) wrapped inside a horizontal scroll which is inside a vertical scroll. I am using the Dpad to navigate across the grid. This works well, as I press the right arrow pad the grid cells move left to right as expected and right to left as left arrow pad is pressed. I have added an onKeylistener attached to each textView of the grid, as I scroll across the grid I am changing the color back ground. The problem is that the onKeyListener apparently takes over the control of the grid. The scroll right works for changing the color but the cells no longer move on the grid. Once I get to last visible column focus continues off screen but the cells stay off screen. Is there a way to implement the scroll inside the onkey event so the cells shift and I have control over the properties of the cell? Or is there a totally different way of doing making this work?
The main components are
textViewD.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
if(keyEvent.getAction()==KeyEvent.ACTION_DOWN && i == KeyEvent.KEYCODE_DPAD_RIGHT) {
gridLayoutE.getChildAt(childIndex[0]).setBackgroundColor(Color.BLACK);
gridLayoutE.getChildAt(childIndex[0] + 1).setBackgroundColor(Color.GREEN);
childIndex[0] = childIndex[0] + 1;
gridLayoutE.requestFocus();
return true;
}
return false;
}
The scrolllistener, I have a class that extends the horizontalscroll in order to have my header table scroll along with my grid. This works.
#Override
public void onScrollChanged (ObservableScrollView scrollView,int x, int y, int oldx, int oldy){
if (scrollView == hsvHeader) {
hsvBody.scrollTo(x, y);
} else if (scrollView == hsvBody) {
hsvHeader.scrollTo(x, y);
}
}
I was able to find a solution to my problem by using dispatchKeyEvent(KeyEvent event). I ended up with something like
public boolean dispatchKeyEvent(KeyEvent event) {
mcols= mcols + 1;
if(event.getAction()==KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
gridLayoutE.getFocusedChild().setBackgroundColor(Color.BLACK);
gridLayoutE.findViewById(mcol).setBackgroundColor(Color.GREEN);
}
return super.dispatchKeyEvent(event);
}
This along with my scrolllistener, allowed my to scroll the grid and change each cell as I did.
How can I limit the scrolling ability of a vertical Recyclerview to only allow scrolling down?
I want to make somehow a "list with no return to the top".
EDIT: It's not a duplicate. I don't want to disable scrolling vertically. I just want to disable scrolling upwards.
I figured out a solution using an OnItemTouchListener.
The scrolling event consists of 3 MotionEvents : ACTION_DOWN , ACTION_MOVE and ACTION_UP.
So on ACTION_DOWN we get the vertical position of the cursor (Y) and the on ACTION_MOVE we compare the new position to the old one.
By returning true, the method onInterceptTouchEvent() makes sure we intercept the scrolling event.
float lastY;
recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
#Override
public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent event) {
int action = event.getAction();
if(action == MotionEvent.ACTION_DOWN) {
lastY = event.getY();
}
if (action == MotionEvent.ACTION_MOVE && event.getY() > lastY) {
return true;
}
return false;
}
...
I have successfully created a list (LinearLayout) that contains multiple dynamic elements/rows. It is filled with content received by webservice.
One of the elements per row is a HorizontalScrollView that contains a variable amount of EditText fields.
That means that all edittexts from all rows (including a header) can scroll with that horizontalScrollView.
A Scrollmanager will make sure that all horizontalScrollviews move simultaneously. So it is basically a scrollable column within a list.
The problem that i am experiencing is as follows.
When i select a EditText view it will show the keyboard, which is what i want it to do. But the scrollManager is triggered so it will scroll all horizontalscrollviews to the end. Instead of keeping the focussed edittext in screen, it will move out sight.
My ScrollManager OnScrollChanged
#Override
public void onScrollChanged(View sender, int l, int t, int oldl, int oldt) {
// avoid notifications while scroll bars are being synchronized
if (isSyncing)
return;
isSyncing = true;
// remember scroll type
if (l != oldl)
scrollType = SCROLL_HORIZONTAL;
else if (t != oldt)
scrollType = SCROLL_VERTICAL;
else {
// not sure why this should happen
isSyncing = false;
return;
}
// update clients
for (ScrollNotifier client : clients) {
View view = (View) client;
if (view == sender)
continue; // don't update sender
// scroll relevant views only
// TODO Add support for horizontal ListViews - currently weird things happen when ListView is being scrolled horizontally
if ((scrollType == SCROLL_HORIZONTAL && view instanceof HorizontalScrollView)
|| (scrollType == SCROLL_VERTICAL && view instanceof ScrollView)
|| (scrollType == SCROLL_VERTICAL && view instanceof ListView)) {
view.scrollTo(l, t);
}
}
isSyncing = false;
}
I the end i want the keyboard to appear and the scrollview to be able to scroll, but i want to prevent the horizontal scroll event when the keyboard appears.
I haven't tested this yet, but you should be able to stop propogation of the touch event to the HorizontalScrollView by setting an OnTouchListener to your EditText and overriding the OnTouch method like this:
#Override
public boolean onTouch(View v, MotionEvent event)
{
Log.i("OnTouch", "Fired On Touch in " + v.getId());
// Do this on the down event too so it's not getting fired before up event
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
// Disallow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(true);
}
//only do this on the up event so you're not doing it for down too
if(event.getAction() == MotionEvent.ACTION_UP)
{
// Disallow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(true);
}
//Keep going with the touch event to show the keyboard
v.onTouchEvent(event);
return true;
}
I Am using Horizontal Scroll View and its work fine , i have 2 web view inside my horizontal Scroll View , my problem is that when u scroll down or up in the web view sometime it go left or right to the other web view because of the Horizontal Scroll View , i use this code
HorizontalScrollView hv = (HorizontalScrollView)findViewById(R.id.horizontalScrollView2);
webView22.setOnTouchListener(new View.OnTouchListener() {
private String TAG;
public boolean onTouch(View v, MotionEvent event) {
Log.v(TAG,"PARENT TOUCH");
findViewById(R.id.webView22).getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
});
webView22.setOnTouchListener(new View.OnTouchListener() {
private String TAG;
public boolean onTouch(View v, MotionEvent event)
{
Log.v(TAG,"CHILD TOUCH");
// Disallow the touch request for parent scroll on touch of child view
findViewById(R.id.webView23).getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
when i use the TAG i got error say The field FragmentActivity.TAG is not visible
but when i add the private String TAG; it go , anyway i am not sure if it is correct ,
How ever after i test it the webView22 is good and great but if i want to go right and left using the Horizontal it will not be work ,
i tried to change the CHILD TOUCH to BUTTON_BACK but still the same . i feel that tag not doing anything it just go to
findViewById(R.id.webView22).getParent().requestDisallowInterceptTouchEvent(false);
return false;
i hope some one can help
Actually, the case here is that both the horizontal scroll view and the web view consume the scroll event. Now it will be hard for the Android OS to decide onto which listener should this scroll event go to.
An option to this will be using Slide animations instead of a horizontal scroll view.
fragmentTransaction.setCustomAnimations(R.anim.slide_bottom_in,
R.anim.slide_bottom_out,R.anim.slide_bottom_in,
R.anim.slide_bottom_out);
But these will again need to be triggered with some event. like button click.
Or you can go for the view pager implementation.
i had the same issue, there are some ways, but many of them require changing the layout, wrong data is send to the child of HorizontalScrollView mostly in the cases where there are children inside the child of HorizontalScrollView.
So i did things like this, instead of:
val childA = horizontalScrollView.getChild(0)
childA.setOnTouchListener(this)
do
horizontalScrollView.setOnTouchListener(this)
This way you have to calculate the clicks, and calls, but it works better than before to me, the scroll was slow and glitchy before.
var touchHistory=0
var lastTouchX=0f
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
if (event == null || v == null) return false
when {
event.action == MotionEvent.ACTION_DOWN -> {
touchHistory=0
lastTouchX=event.x
// Screen1=0-480 / Screen2=0-2304 / ScrollPos=0-2304
// Sample: 240 480 - 576 2304 = 240+576 = result
val child = (v as HorizontalScrollView).getChildAt(0)
val sXCurrent = event.x.toDouble()
val sXMax = v.width.toDouble()
val s2XCurrent = scrollX
val s2XMax = child.width
val clickPos = s2XCurrent+sXCurrent
val result = clickPos/s2XMax
// Do something
}
event.action == MotionEvent.ACTION_UP -> {
if(touchHistory==0){
//Click performed, do something
}
//if touchHistory > 0 is currently scrolling and click is omitted
lastTouchX=event.x
touchHistory=0
}
event.action == MotionEvent.ACTION_MOVE -> {
if(event.x!=lastTouchX)touchHistory++ //Check wether is clicking or scrolling
lastTouchX=event.x
}
}
return false
}
How can I implement a custom onInterceptTouchEvent() in a ListView that give the scrolling priority to the child's of the ListView and as soon as they did their scrolling , give it back to the ListView ? I want to give priority to the inner views.
Try overriding onInterceptTouchEvent() of your children like this:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(!isAtTop && !isAtBottom){
getParent().requestDisallowInterceptTouchEvent(true);
}
return super.onInterceptTouchEvent(ev);
}
In onInterceptTouchEvent() calculate if the ListView has scrolled totally to the top or bottom. If it is somewhere in between then ask the parent to not intercept touches.
To check for top or bottom try:
int scrollRange = computeVerticalScrollRange();
int scrollOffset = computeVerticalScrollOffset();
int scrollExtend = computeVerticalScrollExtent();
if(scrollOffset == 0){
//AtTop
}else if(scrollRange == scrollOffset + scrollExtend){
//AtBottom
}