android listview fling animation is too fast - android

I have a custom ListView that shows items "page by page". So I've written OnTouch method and it works perfect, now I want to write OnFling method that will realize smooth and inertial scrolling of my ListView.
The problem is the scrolling animation of smoothScrollToPosition(int position) isn't smooth, it's very fast. smoothScrollToPosition(int position, int offset, int duration) works, but my minSdk must be 8 and besides this functions place current element badly despite on offset.
This is the code of my OnFling method:
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
if(null == getAdapter()) return false;
boolean returnValue = false;
float pty1 = 0, pty2 = 0;
if (e1 == null || e2 == null)
return false;
pty1 = e1.getY();
pty2 = e2.getY();
if (pty1 - pty2 > swipeMinDistance && Math.abs(velocityY) > swipeThresholdVelocity)
{
float currentVelocity = Math.min(Math.abs(velocityY), Math.abs(swipeMaxVelocity));
final int shift = (int) ((currentVelocity / swipeMaxVelocity) * swipeMaxElements + 1);
if (activeItem < getAdapter().getCount() - shift - 1)
activeItem = activeItem + shift;
else
{
activeItem = getAdapter().getCount() - 1;
return false;
}
returnValue = true;
}
else if (pty2 - pty1 > swipeMinDistance && Math.abs(velocityY) > swipeThresholdVelocity)
{
float currentVelocity = Math.min(Math.abs(velocityY), Math.abs(swipeMaxVelocity));
final int shift = (int) ((currentVelocity / swipeMaxVelocity) * swipeMaxElements + 1);
if (activeItem >= shift)
activeItem = activeItem - shift;
else
{
activeItem = 0;
return false;
}
returnValue = true;
}
smoothScrollToPosition(activeItem);
return returnValue;
}
The xml content is:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rl_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<konteh.example.errortest.CardsListView
android:id="#+id/cardsListView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:smoothScrollbar="true"
/>

Solved! My solution is:
int pixelCount = height * shift * (isForward ? 1 : -1); // calculate approximately shift in pixels
smoothScrollBy(pixelCount, 2 * DURATION * shift);//this smooth scroll works!
postDelayed(new Runnable()
{
public void run()
{
smoothScrollBy(0, 0); // Stops the listview from overshooting.
smoothScrollToPosition(activeItem + 1);
}
},
DURATION * shift);
Maybe it's not the best solution, but it works!

Related

Synchronized scrolling of multiple custom views

I've got custom view, which draws a scale and handles touch event (horizontall scroll of that scale). Drawing part:
#Override
public void onDraw(Canvas canvas){
startingPoint = mainPoint;
counter = 0;
int i = 0;
while (startingPoint <= scaleWidth) {
if(i % 4 == 0) {
size = scaleHeight / 4;
if (counter < 24) {
counter = counter + 1;
} else {
counter = 1;
}
String c = Integer.toString(counter);
canvas.drawText(c, startingPoint, endPoint - (size + 20), textPaint);
} else {
if(i % 2 == 0) {
size = scaleHeight / 8;
} else {
size = scaleHeight / 16;
}
}
if(startingPoint >= closest) {
//метки шкалы
canvas.drawLine(startingPoint, endPoint - size, startingPoint, endPoint, rulerPaint);
}
startingPoint = startingPoint + pxmm;
i = i + 1;
}
}
And TouchEvent:
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float x = event.getX();
prevx = x;
System.out.println(x);
break;
case MotionEvent.ACTION_MOVE:
float z = event.getX();
float dist = z - prevx;
mainPoint = prevsp + dist;
closest = mainPoint - ((int)(mainPoint / pxmm)) * pxmm;
break;
case MotionEvent.ACTION_UP:
float y = event.getX();
prevsp = mainPoint;
break;
}
invalidate();
return true;
}
In activity_main.xml i paste two copies of this view:
<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:orientation="vertical"
android:gravity="center|top">
<net.manualuser.calibr.TimeScale
android:id="#+id/my_scale"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white" />
<net.manualuser.calibr.TimeScale
android:id="#+id/my_scale1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white" />
What should i do to synchronize these two views so that when i scroll either scale another one moves simultaneously. If i'm correct i need to set OnTouchListener for my custom views in MainActivity:
TimeScale defScale = (TimeScale)findViewById(R.id.my_scale);
TimeScale defScale1 = (TimeScale)findViewById(R.id.my_scale1);
defScale.setOnTouchListener(this);
defScale1.setOnTouchListener(this);
And write some code to synchronize them in onTouch method (in MainActivity). But i have now idea how do i do it?
To get it working i need to pass event to both of my views in onTouch in MainActivity:
public boolean onTouch(View v, MotionEvent event){
defScale.onTouchEvent(event);
defScale1.onTouchEvent(event);
return false;
}

How to detect ScrollView scroll by desired pixel value

I'm extending ScrollView to detect if it was scrolled up or down. I want to add option to detect scroll only if it was scrolled by let's say 50 pixels. How to do that? My current code of scrollview overrides onScrollChanged:
public interface OnDetectScrollListener {
void onUpScroll();
void onDownScroll();
}
public class MyScrollView extends ScrollView {
private OnDetectScrollListener onDetectScrollListener = null;
private boolean scrollDown = false;
private boolean scrollUp = false;
.... constructors ....
public void setOnDetectScrollListener(OnDetectScrollListener scrollViewListener) {
this.onDetectScrollListener = scrollViewListener;
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (onDetectScrollListener != null) {
View view = (View) getChildAt(getChildCount() - 1);
int diff = (view.getBottom() - (getHeight() + getScrollY()));
//scroll down
if(t > oldt){
if(!scrollDown & oldt >= 0 & t >= 0){
scrollDown = true;
scrollUp = false;
onDetectScrollListener.onDownScroll();
}
}
//scroll up
else if(t < oldt & diff > 0){
if(!scrollUp){
scrollUp = true;
scrollDown = false;
onDetectScrollListener.onUpScroll();
}
}
}
}
}
I don't have much experience in ScrollView but you can do this:
if what you want is to start scrolling only after 50 pixels you can follow this logic:
bool scrolling = false;
int scrollX = -1;
int scrollY = -1;
protected void scroll(int x, int y)
{
//View was not scrolling
if (scrollX == -1)
{
//Save starting point
scrollX = x;
}
//View keeps scrolling
else
{
//User is touching 50 pixels left from starting point
if (x -scrollX > 50)
{
scrolling = true;
} else
//User is touching 50 pixels right from starting point
if (scrollX -x > 50)
{
scrolling = true;
}
}
if (scrolling)
{
/* Your code */
}
}
I'm not sure if either l or t is x or y on your onScrollView (I've never touched it) but you can implement it your way.
Feel free to create separate variables for scrolling left and scrolling right (or up/down).
Please avoid using pixels, especially for input. Prefer using density-independent pixels (dp). More info here (Supporting Multiple Screens.

check if a Translating View has reached edge of parent?

I have a view that is been dragged around the screen. how do i check if the view has reached the edge of its parent right before applying the next transformation?
full class https://github.com/thuytrinh/android-collage-views/blob/master/libraries/collage-views/src/main/java/com/thuytrinh/android/collageviews/MultiTouchListener.java
here is my current code:
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position.
int pointerIndex = event.findPointerIndex(mActivePointerId);
if (pointerIndex != -1) {
float currX = event.getX(pointerIndex);
float currY = event.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a
// gesture.
if (!mScaleGestureDetector.isInProgress()) {
adjustTranslation(view, currX - mPrevX, currY - mPrevY);
}
}
break;
}
private static void adjustTranslation(View view, float deltaX, float deltaY) {
float[] deltaVector = {deltaX, deltaY};
view.getMatrix().mapVectors(deltaVector);
view.setTranslationX(view.getTranslationX() + deltaVector[0]);
view.setTranslationY(view.getTranslationY() + deltaVector[1]);
}
xml: the view been dragged around is the Collage view(with id "CollageView1"
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:background="#545454">
<FrameLayout
android:layout_width="295dp"
android:layout_height="170dp"
android:splitMotionEvents="true" >
<ImageView
android:id="#+id/collageBgView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="#868686" />
<com.thuytrinh.android.collageviews.CollageView
android:id="#+id/collageView1"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="#drawable/stamp_003" />
</FrameLayout>
</RelativeLayout>
A check has to be made for X ad Y translations, here is the code:
for X translations:
private static boolean hasReachedEdgeX(View v, float newTransX){
if(canCrossParentBoundries){
return false;
}
Boolean reachedEdge = true;
int adjustedWidth = (int) ((v.getWidth() * v.getScaleX() - v.getWidth()) / 2.f);
int viewLeft = v.getLeft() - adjustedWidth;
int viewRight = v.getRight() + adjustedWidth;
View p = (View) v.getParent();
ViewGroup.MarginLayoutParams pLayoutParams = (ViewGroup.MarginLayoutParams) p.getLayoutParams();
int pLeft = p.getLeft() - pLayoutParams.leftMargin;
int pRight = p.getRight() - pLayoutParams.leftMargin - pLayoutParams.rightMargin;
float newViewRight = viewRight + newTransX;
float newViewLeft = viewLeft + newTransX;
//checks if the view has reached the boundaries of its parent
if((newViewLeft > pLeft) && (newViewRight < pRight)){
reachedEdge = false;
}
return reachedEdge;
}
For Y translations:
private static boolean hasReachedEdgeY(View v, float newTransY){
if(canCrossParentBoundries){
return false;
}
Boolean reachedEdge = true;
int adjustedHeight = (int) ((v.getHeight() * v.getScaleY() - v.getHeight()) / 2.f);
int viewTop = v.getTop() - adjustedHeight;
int viewBottom = v.getBottom() + adjustedHeight;
View p = (View) v.getParent();
ViewGroup.MarginLayoutParams pLayoutParams = (ViewGroup.MarginLayoutParams) p.getLayoutParams();
int parentTop = p.getTop() - pLayoutParams.topMargin;
int parentBottom = p.getBottom() - pLayoutParams.topMargin - pLayoutParams.bottomMargin;
float newViewTop = viewTop + newTransY;
float newViewBottom = viewBottom + newTransY;
//checks if the view has reached the boundaries of its parent
if((newViewBottom < parentBottom) && (newViewTop > parentTop)){
reachedEdge = false;
}
return reachedEdge;
}
I hope that helps who ever is also tackling this problem. For full code head over to the github ripo:
https://github.com/kcochibili/android-collage-views/blob/master/libraries/collage-views/src/main/java/com/thuytrinh/android/collageviews/MultiTouchListener.java

Help with Android UI ListView problems

To understand this question, first read how this method works.
I am trying to implements a drag and drop ListView, it's going alright but have run into
a road block. So I don't have to handled everything, I am intercepting(but returning false) MotionEvents sent to the ListView, letting it handle scrolling and stuff. When I want to start dragging a item, I then return true and handled all the dragging stuff. Everything is working fine except for one thing. The drag(drag and drop) is started when it is determined that a long press as a occurred(in onInterceptTouchEvent). I get the Bitmap for the image that I drag around like so. itemPositition being the index of the item that was selected.
(omitting irrelevant parts)
...
View dragItem = mListView.getChildAt(itemPosition);
dragItem.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(dragItem.getDrawingCache());
mDragImage = new ImageView(mContext);
mDragImage.setImageBitmap(bitmap);
...
The problem is, mDragImage is a solid black like this.
But, if I don't let ListView handle anything. As in, I start the drag on ACTION_DOWN and stop on ACTION_UP, mDragImage looks has expected(but I obviously lose scrolling abilities).
Since the drag is started with a long press, the ListView is given the opportunity to do things before the long press occurs. This is my guess as to why this is happening. When a item is pressed, it is highlighted by the ListView. Somewhere in doing so, it is messing with the bitmap. So when I go to get it, it's in a weird state(all black).
I see two options for fixing this, neither of which I know how to do.
Create a image from scratch.
Handle the highlighting myself(if that is the problem).
Option two seems a better one to me, except that I looked at the documentation and the source code and could not find out how to do so. Here are some things I have done/tried.
I set setOnItemClickListener(...) and
setOnItemSelectedListener(...) with a empty method(highlighting
still happens). (Before anyone suggests it, calling
setOnClickListener results in a runtime error.)
I also looked into trying to get the ListView to make a new item
(for option 2), but could not find a way.
Spent 45ish minutes looking through the source code and
documentation trying to pinpoint where the highlighting was
happening(I never found it).
Any help fixing this would be appreciated.
(EDIT1 START)
So I don't actually know if onLongClickListener is working, I made an error before thinking it was. I am trying to set it up right now, will update when I find out if it does.
(EDIT1 END)
Last minute edit before post. I tried using onLongClickListener just now, and the image is good. I would still like to know if there is another way. How I have to use onLongClickListener to get things working is ugly, but it works. I also spent so much time trying to figure this out, it would be nice to find out the answer. I still want to be able to change/handle the highlight color, the default orangeish color is not pretty. Oh and sorry about the length of the post. I could not think of way of making it shorter, while supplying all the information I thought was needed.
use this code, it's allows operation drug and drop in ListView:
public class DraggableListView extends ListView {
private static final String LOG_TAG = "tasks365";
private static final int END_OF_LIST_POSITION = -2;
private DropListener mDropListener;
private int draggingItemHoverPosition;
private int dragStartPosition; // where was the dragged item originally
private int mUpperBound; // scroll the view when dragging point is moving out of this bound
private int mLowerBound; // scroll the view when dragging point is moving out of this bound
private int touchSlop;
private Dragging dragging;
private GestureDetector longPressDetector;
public DraggableListView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.listViewStyle);
}
public DraggableListView(final Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
longPressDetector = new GestureDetector(getContext(), new SimpleOnGestureListener() {
#Override
public void onLongPress(final MotionEvent e) {
int x = (int) e.getX();
final int y = (int) e.getY();
int itemnum = pointToPosition(x, y);
if (itemnum == AdapterView.INVALID_POSITION) {
return;
}
if (dragging != null) {
dragging.stop();
dragging = null;
}
final View item = getChildAt(itemnum - getFirstVisiblePosition());
item.setPressed(false);
dragging = new Dragging(getContext());
dragging.start(y, ((int) e.getRawY()) - y, item);
draggingItemHoverPosition = itemnum;
dragStartPosition = draggingItemHoverPosition;
int height = getHeight();
mUpperBound = Math.min(y - touchSlop, height / 3);
mLowerBound = Math.max(y + touchSlop, height * 2 / 3);
}
});
setOnItemLongClickListener(new OnItemLongClickListener() {
#SuppressWarnings("unused")
public boolean onItemLongClick(AdapterView<?> paramAdapterView, View paramView, int paramInt, long paramLong) {
// Return true to let AbsListView reset touch mode
// Without this handler, the pressed item will keep highlight.
return true;
}
});
}
/* pointToPosition() doesn't consider invisible views, but we need to, so implement a slightly different version. */
private int myPointToPosition(int x, int y) {
if (y < 0) {
return getFirstVisiblePosition();
}
Rect frame = new Rect();
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
child.getHitRect(frame);
if (frame.contains(x, y)) {
return getFirstVisiblePosition() + i;
}
}
if ((x >= frame.left) && (x < frame.right) && (y >= frame.bottom)) {
return END_OF_LIST_POSITION;
}
return INVALID_POSITION;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (longPressDetector.onTouchEvent(ev)) {
return true;
}
if ((dragging == null) || (mDropListener == null)) {
// it is not dragging, or there is no drop listener
return super.onTouchEvent(ev);
}
int action = ev.getAction();
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
dragging.stop();
dragging = null;
if (mDropListener != null) {
if (draggingItemHoverPosition == END_OF_LIST_POSITION) {
mDropListener.drop(dragStartPosition, getCount() - 1);
} else if (draggingItemHoverPosition != INVALID_POSITION) {
mDropListener.drop(dragStartPosition, draggingItemHoverPosition);
}
}
resetViews();
break;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
int x = (int) ev.getX();
int y = (int) ev.getY();
dragging.drag(x, y);
int position = dragging.calculateHoverPosition();
if (position != INVALID_POSITION) {
if ((action == MotionEvent.ACTION_DOWN) || (position != draggingItemHoverPosition)) {
draggingItemHoverPosition = position;
doExpansion();
}
scrollList(y);
}
break;
}
return true;
}
private void doExpansion() {
int expanItemViewIndex = draggingItemHoverPosition - getFirstVisiblePosition();
if (draggingItemHoverPosition >= dragStartPosition) {
expanItemViewIndex++;
}
// Log.v(LOG_TAG, "Dragging item hovers over position " + draggingItemHoverPosition + ", expand item at index "
// + expanItemViewIndex);
View draggingItemOriginalView = getChildAt(dragStartPosition - getFirstVisiblePosition());
for (int i = 0;; i++) {
View itemView = getChildAt(i);
if (itemView == null) {
break;
}
ViewGroup.LayoutParams params = itemView.getLayoutParams();
int height = LayoutParams.WRAP_CONTENT;
if (itemView.equals(draggingItemOriginalView)) {
height = 1;
} else if (i == expanItemViewIndex) {
height = itemView.getHeight() + dragging.getDraggingItemHeight();
}
params.height = height;
itemView.setLayoutParams(params);
}
}
/**
* Reset view to original height.
*/
private void resetViews() {
for (int i = 0;; i++) {
View v = getChildAt(i);
if (v == null) {
layoutChildren(); // force children to be recreated where needed
v = getChildAt(i);
if (v == null) {
break;
}
}
ViewGroup.LayoutParams params = v.getLayoutParams();
params.height = LayoutParams.WRAP_CONTENT;
v.setLayoutParams(params);
}
}
private void resetScrollBounds(int y) {
int height = getHeight();
if (y >= height / 3) {
mUpperBound = height / 3;
}
if (y <= height * 2 / 3) {
mLowerBound = height * 2 / 3;
}
}
private void scrollList(int y) {
resetScrollBounds(y);
int height = getHeight();
int speed = 0;
if (y > mLowerBound) {
// scroll the list up a bit
speed = y > (height + mLowerBound) / 2 ? 16 : 4;
} else if (y < mUpperBound) {
// scroll the list down a bit
speed = y < mUpperBound / 2 ? -16 : -4;
}
if (speed != 0) {
int ref = pointToPosition(0, height / 2);
if (ref == AdapterView.INVALID_POSITION) {
//we hit a divider or an invisible view, check somewhere else
ref = pointToPosition(0, height / 2 + getDividerHeight() + 64);
}
View v = getChildAt(ref - getFirstVisiblePosition());
if (v != null) {
int pos = v.getTop();
setSelectionFromTop(ref, pos - speed);
}
}
}
public void setDropListener(DropListener l) {
mDropListener = l;
}
public interface DropListener {
void drop(int from, int to);
}
class Dragging {
private Context context;
private WindowManager windowManager;
private WindowManager.LayoutParams mWindowParams;
private ImageView mDragView;
private Bitmap mDragBitmap;
private int coordOffset;
private int mDragPoint; // at what offset inside the item did the user grab it
private int draggingItemHeight;
private int x;
private int y;
private int lastY;
public Dragging(Context context) {
this.context = context;
windowManager = (WindowManager) context.getSystemService("window");
}
/**
* #param y
* #param offset - the difference in y axis between screen coordinates and coordinates in this view
* #param view - which view is dragged
*/
public void start(int y, int offset, View view) {
this.y = y;
lastY = y;
this.coordOffset = offset;
mDragPoint = y - view.getTop();
draggingItemHeight = view.getHeight();
mDragView = new ImageView(context);
mDragView.setBackgroundResource(android.R.drawable.alert_light_frame);
// Create a copy of the drawing cache so that it does not get recycled
// by the framework when the list tries to clean up memory
view.setDrawingCacheEnabled(true);
mDragBitmap = Bitmap.createBitmap(view.getDrawingCache());
mDragView.setImageBitmap(mDragBitmap);
mWindowParams = new WindowManager.LayoutParams();
mWindowParams.gravity = Gravity.TOP;
mWindowParams.x = 0;
mWindowParams.y = y - mDragPoint + coordOffset;
mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
mWindowParams.format = PixelFormat.TRANSLUCENT;
mWindowParams.windowAnimations = 0;
windowManager.addView(mDragView, mWindowParams);
}
public void drag(int x, int y) {
lastY = this.y;
this.x = x;
this.y = y;
mWindowParams.y = y - mDragPoint + coordOffset;
windowManager.updateViewLayout(mDragView, mWindowParams);
}
public void stop() {
if (mDragView != null) {
windowManager.removeView(mDragView);
mDragView.setImageDrawable(null);
mDragView = null;
}
if (mDragBitmap != null) {
mDragBitmap.recycle();
mDragBitmap = null;
}
}
public int getDraggingItemHeight() {
return draggingItemHeight;
}
public int calculateHoverPosition() {
int adjustedY = (int) (y - mDragPoint + (Math.signum(y - lastY) + 2) * draggingItemHeight / 2);
// Log.v(LOG_TAG, "calculateHoverPosition(): lastY=" + lastY + ", y=" + y + ", adjustedY=" + adjustedY);
int pos = myPointToPosition(0, adjustedY);
if (pos >= 0) {
if (pos >= dragStartPosition) {
pos -= 1;
}
}
return pos;
}
}
}

Android with SeekBar - Prevent 'thumb' from moving when touching SeekBar to drag/scroll screen

I've got an android app with a TableLayout with each row having a SeekBar and a ToggleButton. The rows go beyond the visible range of the screen so it's scrollable. When I touch and drag to scroll the page up and in the process touch the SeekBar, it immediately changes the position of the "thumb" instead of scrolling the page. However, the ToggleButton does not behave this way; instead, I can start the drag on the button and scroll then release without the ToggleButton changing state.
Is there anyway to get the SeekBar to behave this way, such that touching it to start a drag will not cause the bar to change positions but instead scroll the page?
I've had similar problems. The only solution I could find (when the seekbar is in a listview) was to disable the seekbar until the item is clicked.
Solution
In the ArrayAdapter I set both enabled and focusable to false and added the SeekBar listener, setting the attributes to false allowed me to use the list item onItemClicked listener. Inside the onItemCLickListener I retreived the seekbar, set the attributes to true, this meant it could be slid up or down. I then disabled it after the adjustment had been made. code below
ArrayAdapter Snippet
this code is inside the creation of the list item, in which the seekbar is housed
seekBar.setClickable(false);
seekBar.setFocusable(false);
seekBar.setEnabled(false);
/* attach listener */
attachProgressUpdatedListener(seekBar, positionOfItemInList);
AttachProgressUpdatedListener
this method attaches the listener to the seekbar, inside the arrayAdapter class
private void attachProgressUpdatedListener(SeekBar seekBar,
final int position) {
seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
public void onStopTrackingTouch(SeekBar seekBar) {
int progress = seekBar.getProgress();
int task_id = (Integer) seekBar.getTag();
TaskHandler taskHandler = new TaskHandler(DBAdapter
.getDBAdapterInstance(getContext()));
taskHandler.updateTaskProgress(task_id, progress);
mList.get(position).setProgress(progress);
//need to fire an update to the activity
notifyDataSetChanged();
seekBar.setEnabled(false);
}
public void onStartTrackingTouch(SeekBar seekBar) {
// empty as onStartTrackingTouch listener not being used in
// current implementation
}
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// empty as onProgressChanged listener not being used in
// current implementation
}
});
}
OnItemCLickListener
this snipped is from the activity which houses the list view.
taskListView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
SeekBar sb = (SeekBar) view.findViewById(R.id.seek);
sb.setFocusable(true);
sb.setEnabled(true);
}
});
I got it working by subclassing the seekbar and checking if the angle of motion is greater than 45 degrees. In that case ignore touches and return false so scroll view will do its thing.
Edit:
Here is an extended SeekBar which allows you to set a min/max in floats rather than integers.
public class SeekBarEx extends SeekBar implements
SeekBar.OnSeekBarChangeListener {
final int SEEK_POINTS = 0x10000;
final String TAG = "SeekBarEx";
public float mMax;
public float mMin;
public OnSeekBarExChangeListener delegate = null;
public interface OnSeekBarExChangeListener {
public void onSeekChanged(SeekBarEx seekBarEx, float value,
boolean fromUser);
public void onStartTrackingTouch(SeekBarEx seekBar);
public void onStopTrackingTouch(SeekBarEx seekBar);
}
public SeekBarEx(Context ctx, AttributeSet attr) {
super(ctx, attr);
super.setMax(SEEK_POINTS);
mMin = 0f;
mMax = 1.0f;
initAttributes(attr);
this.setOnSeekBarChangeListener(this);
}
public void setDelegate(OnSeekBarExChangeListener d) {
delegate = d;
}
public void initAttributes(AttributeSet attrSet) {
TypedArray a;
a = getContext().obtainStyledAttributes(attrSet, R.styleable.SeekBarEx);
final int N = a.getIndexCount();
int i;
for (i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.SeekBarEx_max:
mMax = a.getFloat(i, 1.0f);
Log.d(TAG, "maxSet " + mMax);
break;
case R.styleable.SeekBarEx_min:
mMin = a.getFloat(i, 0f);
Log.d(TAG, "minSet" + mMin);
break;
case R.styleable.SeekBarEx_value:
this.setValue(a.getFloat(i, 0));
break;
}
}
a.recycle();
}
#Override
public int getProgress() {
return super.getProgress();
}
public float getValue() {
float r;
float run;
r = (float) super.getProgress();
r = r / (float) SEEK_POINTS;
run = mMax - mMin;
r = r * run + mMin;
return r;
}
public void setValue(float v) {
if (Float.isNaN(v) || Float.isInfinite(v))
return;
if (v > mMax)
v = mMax;
if (v < mMin)
v = mMin;
float run;
int setv;
run = mMax - mMin;
v -= mMin;
setv = Math.round(v * (float) SEEK_POINTS / run);
super.setProgress(setv);
}
public boolean valueChanged = false;
public void cancelTracking() {
if (oldValue != Float.NaN) {
this.setValue(oldValue);
oldValue = Float.NaN;
valueChanged = false;
acceptTouches = false;
acceptChange = false;
}
}
// we override these methods so that when we forcully cancel
// on ontouches moved. We can revert back to the old value
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
Log.d(TAG, "SeekBar changed to " + progress);
if (delegate != null && acceptTouches) {
valueChanged = true;
delegate.onSeekChanged(this, this.getValue(), fromUser);
} else
cancelTracking();
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
if (delegate != null)
delegate.onStartTrackingTouch(this);
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (delegate != null && valueChanged)
delegate.onStopTrackingTouch(this);
else
cancelTracking();
acceptChange = false;
valueChanged = false;
}
public float mY, mX;
public boolean acceptTouches = true;
// acceptChange never read todo: delete
public boolean acceptChange = false;
public float oldValue = Float.NaN;
public ScrollView getScrollView() {
View view;
view = this;
int maxUp;
maxUp = 5;
while (view != null && maxUp > 0) {
view = (View) view.getParent();
ScrollView scroller;
if (view instanceof ScrollView) {
scroller = (ScrollView) view;
return scroller;
}
maxUp--;
}
return null;
}
// **************************************
// This is the important part in achieving the effect in scroll
// view to be nice
#Override
public boolean onTouchEvent(MotionEvent event) {
int action;
action = event.getAction() & MotionEvent.ACTION_MASK;
ScrollView scroller = this.getScrollView();
boolean mayScroll;
mayScroll = true;
if (scroller == null)
mayScroll = false;
else {
int scrollAmount = scroller.getMaxScrollAmount();
if (scrollAmount == 0)
mayScroll = false;
}
switch (action) {
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "got cancel touches");
cancelTracking();
super.onTouchEvent(event);
return true;
case MotionEvent.ACTION_DOWN:
mX = event.getX();
mY = event.getY();
acceptTouches = true;
acceptChange = false;
oldValue = this.getValue();
valueChanged = false;
break;
case MotionEvent.ACTION_MOVE:
float x;
float y;
x = event.getX();
y = event.getY();
float dx;
float dy;
dx = x - mX;
dy = y - mY;
if (dx < 0)
dx = -dx;
if (dy < 0)
dy = -dy;
y = this.getHeight() / 2 - y;
float angle;
float distance;
distance = dx * dx + dy * dy;
// I just realized this is wrong it should be
// angle = (float)Math.acos(Math.abs(dx)/Math.sqrt(distance))
// I'm leaving it until tested or someone can confirm
angle = (float) Math.atan(dy / dx);
int distanceLimit;
distanceLimit = this.getHeight() / 3;
distanceLimit *= distanceLimit;
// if we move at an angle of atleast 45degrees
// cancel
if (mayScroll && angle > Math.PI / 4.0) {
cancelTracking();
}
mX += 100000;
if (y < 0)
y = -y;
// if we moved finger too far just cancel
// cause the person may have wanted to scroll but
// failed so we revert back to the old value
if (y > this.getHeight() * 2) {
cancelTracking();
} else if (acceptTouches)
acceptChange = true;
break;
default:
break;
}
// if we accept touches do the usual otherwise
// return false so scrollView can do it's thing
if (acceptTouches)
return super.onTouchEvent(event);
return false;
}
}
seekbarex.xml is bellow. This is just to add min/max/value as floats.
<declare-styleable name="SeekBarEx">
<attr name="min" format="float"/>
<attr name="max" format="float"/>
<attr name="value" format="float"/>
</declare-styleable>
</resources>

Categories

Resources