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.
Related
I have been working for a day at this and still can't figure it out. I am making an Android app for an Android Wear device and I can't figure out how to detect the rotary wheel on the device to get scroll events. I know that by default the rotary wheel gets assigned to ScrollViews and Listviews, etc. and I've got that to work, but I can't get it to work on a simple NumberPicker. As suggested by Google API's, I've used the OnGenericMotionListener and while it does not throw any errors, it still doesn't pick up anything.
I would like the NumberPicker to scroll with the rotary wheel. My code is as follows. Nothing in logcat to show any errors:
MyActivity.this.setContentView(R.layout.number_picker);
NumberPicker numberPicker = (NumberPicker) findViewById(R.id.numberPicker1);
numberPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
numberPicker.setMaxValue(100);
numberPicker.setMinValue(0);
numberPicker.setWrapSelectorWheel(true);
numberPicker.setOnValueChangedListener( new NumberPicker.
OnValueChangeListener() {
public void onValueChange(NumberPicker picker, int
oldVal, int newVal) {
}
});
numberPicker.setOnGenericMotionListener(new OnGenericMotionListener() {
#Override
public boolean onGenericMotion(View v, MotionEvent event) {
Log.i(TAG, "Received scroll event:" + event.getAction());
switch (event.getAction()) {
case MotionEvent.ACTION_SCROLL:
}
return false;
}
});
I think you're looking for this:
Add custom rotary input scrolling:
If your scrollable view doesn't natively support rotary input
scrolling, or if you want to do something other than scrolling in
response to rotary input events (zoom in/out, turn dials, etc.), you
can use the RotaryEncoder methods in the Wearable Support Library.
The following code snippet shows how to use RotaryEncoder to add
custom scrolling in your app's view:
myView.setOnGenericMotionListener(new View.OnGenericMotionListener() {
#Override
public boolean onGenericMotion(View v, MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_SCROLL && RotaryEncoder.isFromRotaryEncoder(ev)) {
// Don't forget the negation here
float delta = -RotaryEncoder.getRotaryAxisValue(ev) * RotaryEncoder.getScaledScrollFactor(
getContext());
// Swap these axes if you want to do horizontal scrolling instead
scrollBy(0, Math.round(delta));
return true;
}
return false;
}
});
Anyway you can check the full guide about rotary events in android
wear.
The documentation it's a little confusing but it's all there, pay attention to the next paragraph:
Rotary input events are only sent to the focused view. These events
do not bubble up the view hierarchy. If there is no focused view, or
if the focused view returns false from View.onGenericMotionEvent,
then (and only then) the event is sent to
Activity.onGenericMotionEvent.
Basically all you have to do is override onGenericMotionEvent on your Activity like this:
#Override
public boolean onGenericMotion(View v, MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_SCROLL && RotaryEncoder.isFromRotaryEncoder(ev)) {
// Don't forget the negation here
float delta = -RotaryEncoder.getRotaryAxisValue(ev) * RotaryEncoder.getScaledScrollFactor(
getContext());
// Swap these axes if you want to do horizontal scrolling instead
scrollBy(0, Math.round(delta));
return true;
}
return false;
}
And that's it your Activity will receive all the rotary input events.
I have a problem with HorizontalScrollView. I have overriden onScrollChanged with only watching x scroll position value. When I am touching the screen and moving my finger, the value in onScrollChanged is correct. When I release my finger each value appears twice in my logcat. It seems that a position value triggered the event twice after my finger left the screen.
First, I created a class that extends from HorizontalScrollView and overriden onScrollChanged
public class MyHorizontalScrollView extends HorizontalScrollView {
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
Log.i("12345", String.valueOf(x) + " " + String.valueOf(oldx));
}
}
Summary:
When I am touching the screen and moving, each value appears once.
After my finger left the screen, each value apprears twice.
Please help me solve this problem. I want to know where the second call is from.
The reason behind this is that when MotionEvent.ACTION_UP motion event is fired, onScrollChanged is called again. Add these methods to your scroll view to suppress that event, which will fix your problem.
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return event.getAction() == MotionEvent.ACTION_UP || super.onTouchEvent(event);
}
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;
}
Currently I'm using ListView and it's working fine. But I have text in a ListView that is like a paragraph and I just want to show those 2 lines of text and make the rest of the text scrollable, but I'm having an issue that if I make the TextView scrollable inside of the ListView, then the TextView get the focus of its parent (ListView) and won't let it be scrolled.
So can I achieve this scrollable TextView functionality that won't disturb the scrolling property of the ListView?
Thank you.
I was able to do this in the following way:
Into the getView method of the ListAdapter obtain the TextView object of the line, and write
textView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
v.getParent().getParent().requestDisallowInterceptTouchEvent(true);
return false ;
}
});
when you will touch the TextView, take control of the scrolling
What you are trying to achieve really is impossible. How can the OS know that you are trying to scroll the list vs the list row paragraph? You would essentially need to scroll to the bottom of the list row paragraph before the actual list itself could scroll. This is confusing to the user, and not common UX.
I would suggest you look into ExpandableListView. It allows you to have collapsed versions of each row, in your case just 2 lines of text for each list row. When the user taps on the row, it could expand to the full paragraph form, and the list would be scrollable the whole time. There are plenty of tutorials you should be able to find online.
Nothing is Impossible yet way to do things are may b difficult. Directly this thing can`t be achieved but indirectly yes it can be achieved, and yes i achieved.
how did i achieve is a bit complex but yes will share that how did i achieve.
In a ListView when i click on Textview i block the Touch mode of the listView so that their toch method don't intercept each other, and that can be done by using requestDisallowInterceptTouchEvent(true);
this block the TouchListener of the parent (ListView).
Now when click on TextView i allow its touch listener and also setMovementMethod()
but for Movement i made a custom class and Class is Following
public class myScrollMethod extends ScrollingMovementMethod {
#Override
public void onTakeFocus(TextView widget, Spannable text, int dir) {
// TODO Auto-generated method stub
super.onTakeFocus(widget, text, dir);
}
#Override
public boolean onKeyDown(TextView widget, Spannable buffer,
int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_DOWN:
for (int i = 0, scrollAmount = getScrollAmount(widget); i < scrollAmount; i++) {
down(widget, buffer);
}
return true;
case KeyEvent.KEYCODE_DPAD_UP:
for (int i = 0, scrollAmount = getScrollAmount(widget); i < scrollAmount; i++) {
up(widget, buffer);
}
return true;
default:
return super.onKeyDown(widget, buffer, keyCode, event);
}
}
private int getScrollAmount(TextView widget) {
final int visibleLineCount = (int) ((1f * widget.getHeight()) / widget
.getLineHeight());
int scrollAmount = visibleLineCount - 1;
if (scrollAmount < 1) {
scrollAmount = 1;
}
return scrollAmount;
}
}
After that when i click on parent i enable the TouchIntercepter of the parent set true and that get hold on its parent and start scrolling.
By this way i have successfully achieved this requirement
From a simplistic overview I have a custom View that contains some bitmaps the user can drag around and resize.
The way I do this is fairly standard as in I override onTouchEvent in my CustomView and check if the user is touching within an image, etc.
My problem comes when I want to place this CustomView in a ScrollView. This works, but the ScrollView and the CustomView seem to compete for MotionEvents, i.e. when I try to drag an image it either moves sluggishly or the view scrolls.
I'm thinking I may have to extend a ScrollView so I can override onInterceptTouchEvent and let it know if the user is within the bounds of an image not to try and scroll. But then because the ScrollView is higher up in the hierarchy how would I get access to the CustomView's current state?
Is there a better way?
Normally Android uses a long press to begin a drag in cases like these since it helps disambiguate when the user intends to drag an item vs. scroll the item's container. But if you have an unambiguous signal when the user begins dragging an item, try getParent().requestDisallowInterceptTouchEvent(true) from the custom view when you know the user is beginning a drag. (Docs for this method here.) This will prevent the ScrollView from intercepting touch events until the end of the current gesture.
None of the solutions found worked "out of the box" for me, probably because my custom view extends View, not ViewGroup, and thus I can't implement onInterceptTouchEvent.
Also calling getParent().requestDisallowInterceptTouchEvent(true) was throwing NPE, or doing nothing at all.
Finally this is how I solved the problem:
Inside your custom onTouchEvent call requestDisallow... when your view will take care of the event. For example:
#Override
public boolean onTouchEvent(MotionEvent event) {
Point pt = new Point( (int)event.getX(), (int)event.getY() );
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (/*this is an interesting event my View will handle*/) {
// here is the fix! now without NPE
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
clicked_on_image = true;
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (clicked_on_image) {
//do stuff, drag the image or whatever
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
clicked_on_image = false;
}
return true;
}
Now my custom view works fine, handling some events and letting scrollView catch the ones we don't care about. Found the solution here: http://android-devblog.blogspot.com.es/2011/01/scrolling-inside-scrollview.html
Hope it helps.
There is an Android event called MotionEvent.ACTION_CANCEL (value = 3). All I do is override my custom control's onTouchEvent method and capture this value. If I detect this condition then I respond accordingly.
Here is some code:
#Override
public boolean onTouchEvent(MotionEvent event) {
if(isTouchable) {
int maskedAction = event.getActionMasked();
if (maskedAction == MotionEvent.ACTION_DOWN) {
this.setTextColor(resources.getColor(R.color.octane_orange));
initialClick = event.getX();
} else if (maskedAction == MotionEvent.ACTION_UP) {
this.setTextColor(defaultTextColor);
endingClick = event.getX();
checkIfSwipeOrClick(initialClick, endingClick, range);
} else if(maskedAction == MotionEvent.ACTION_CANCEL)
this.setTextColor(defaultTextColor);
}
return true;
}