I'm in my android app I need a viewpager which slide vertically (up
down manner). For this I have made a custom viewpager & in which I'm
using the traditional viewpager & applied PageTransfirmer to make it
swipe vertically not horizontly. Everything is working fine in other
devices except One Plus 5t (andriod version 9)
My code is below:
public class VerticalViewPager extends ViewPager {
private float initialXValue;
private float initialYValue;
private float minXDifference = 200;
private float minYDifference = 100;
public static SwapListener swapListener;
public static String SwipeLeft = "left";
public static String SwipeRight = "right";
public static boolean swipeTriggered = false;
public static boolean verticalSwipeTriggered = false;
private FixedSpeedScroller mScroller = null;
private boolean enabled;
public VerticalViewPager(Context context) {
super(context);
init();
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// The majority of the magic happens here
setPageTransformer(true, new VerticalPageTransformer());
// The easiest way to get rid of the overscroll drawing that
happens on
the left and right
setOverScrollMode(OVER_SCROLL_NEVER);
try {
Class<?> viewpager = ViewPager.class;
Field scroller = viewpager.getDeclaredField("mScroller");
scroller.setAccessible(true);
mScroller = new FixedSpeedScroller(getContext(),
new DecelerateInterpolator());
scroller.set(this, mScroller);
} catch (Exception ignored) {
}
}
/*
* Set the factor by which the duration will change
*/
public void setScrollDuration(int duration) {
mScroller.setScrollDuration(duration);
}
private class FixedSpeedScroller extends Scroller {
private int mDuration = 1000;
public FixedSpeedScroller(Context context) {super(context);}
public FixedSpeedScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}
public FixedSpeedScroller(Context context, Interpolator interpolator,
boolean flywheel) {super(context, interpolator, flywheel);}
#Override
public void startScroll(int startX, int startY, int dx, int dy, int
duration) {
// Ignore received duration, use fixed one instead
super.startScroll(startX, startY, dx, dy, mDuration);
}
#Override
public void startScroll(int startX, int startY, int dx, int dy) {
// Ignore received duration, use fixed one instead
super.startScroll(startX, startY, dx, dy, mDuration);
}
public void setScrollDuration(int duration) {mDuration = duration;}
}
private class VerticalPageTransformer implements
ViewPager.PageTransformer {
#Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
}
else if (position <= 1) {
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
//set Y position to swipe in from top
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
}
else {
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
/**
* Swaps the X and Y coordinates of your touch event.
*/
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev){
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev); // return touch coordinates to original reference frame
for any child views
//IsSwipeAllowed(ev);
return intercepted;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
IsSwipeAllowed(ev);
return super.onTouchEvent(swapXY(ev));
}
private void IsSwipeAllowed(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN) {
initialXValue = event.getX();
initialYValue = event.getY();
}
if(event.getAction()==MotionEvent.ACTION_MOVE) {
try {
float diffX = Math.abs(event.getX() - initialXValue);
float diffy = Math.abs(event.getY() - initialYValue);
if (diffX > diffy && diffX > minXDifference) {
// swipe horizotal
if (!swipeTriggered && event.getX() > initialXValue) {
swapListener.listenSwapEvent(SwipeRight);
swipeTriggered = true;
}
else if (event.getX() < initialXValue) {
if (!HomeScreen.projectName.equals("ABMCPL") &&
CustomViewPager.IsSwipeAllowed(event) && !swipeTriggered) {
swapListener.listenSwapEvent(SwipeLeft); // to webview page
swipeTriggered = true;
}
}
}
else if (diffX < diffy && diffy > minYDifference) {
if (!verticalSwipeTriggered && event.getY() > initialYValue) {
viewPager.setCurrentItem(LandingPage.viewPager.getCurrentItem() - 1);
verticalSwipeTriggered = true;
}
else if (!verticalSwipeTriggered && event.getY() < initialYValue){
verticalSwipeTriggered = true;
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
public interface SwapListener {
void listenSwapEvent (String direction);
}
}
Related
In my application I'm using view pager for vertical scroll for swiping the pages. Vertical scrolling working fine. An issues is While swiping the pages,it is not swiping smoothly , it is stucking while swiping.Can Anyone give me solution for my issue. Thanks in advance.
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
super(context);
init();
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// The majority of the magic happens here
setPageTransformer(true, new VerticalPageTransformer());
// The easiest way to get rid of the overscroll drawing that happens on the left and right
setOverScrollMode(OVER_SCROLL_NEVER);
}
private static class VerticalPageTransformer implements PageTransformer {
private static float MIN_SCALE = 0.95f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
view.setAlpha(1);
//view.setTranslationX(1);
view.setScaleX(1);
view.setScaleY(1);
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
view.setTranslationX(-1 * view.getWidth() * position);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(1 - position);
view.setTranslationX(-1 * view.getWidth() * position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else {
view.setAlpha(0);
}
}
}
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev){
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev);
return intercepted;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
}
I made a rotating knob ,but I want to stop the knob at specific angles for 2 seconds. I want to stop it on 260f and -20f.
Can anyone suggest how to do it ?
This is the code from a blog. I made many changes according to my requirements.
public class RotatoryKnobView extends ImageView {
private float angle = -20f;
private float theta_old=0f;
private RotaryKnobListener listener;
public interface RotaryKnobListener {
public void onKnobChanged(float arg);
}
public void setKnobListener(RotaryKnobListener l )
{
listener = l;
}
public RotatoryKnobView(Context context) {
super(context);
initialize();
}
public RotatoryKnobView(Context context, AttributeSet attrs)
{
super(context, attrs);
initialize();
}
public RotatoryKnobView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
initialize();
}
private float getTheta(float x, float y)
{
float sx = x - (getWidth() / 2.0f);
float sy = y - (getHeight() / 2.0f);
float length = (float)Math.sqrt( sx*sx + sy*sy);
float nx = sx / length;
float ny = sy / length;
float theta = (float)Math.atan2( ny, nx );
final float rad2deg = (float)(180.0/Math.PI);
float thetaDeg = theta*rad2deg;
return (thetaDeg < 0) ? thetaDeg + 360.0f : thetaDeg;
}
public void initialize()
{
this.setImageResource(R.drawable.rotoron);
setOnTouchListener(new OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event) {
float x = event.getX(0);
float y = event.getY(0);
float theta = getTheta(x,y);
switch(event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_POINTER_DOWN:
theta_old = theta;
break;
case MotionEvent.ACTION_MOVE:
invalidate();
float delta_theta = theta - theta_old;
theta_old = theta;
int direction = (delta_theta > 0) ? 1 : -1;
angle += 5*direction;
notifyListener(angle+20);
break;
}
return true;
}
});
}
private void notifyListener(float arg)
{
if (null!=listener)
listener.onKnobChanged(arg);
}
protected void onDraw(Canvas c)
{if(angle==257f){
try {
synchronized (c) {
c.wait(5000);
angle=260f;
}
} catch (InterruptedException e) {
}
}
else if(angle==-16f)
{
try {
synchronized (c) {
c.wait(5000);
angle=-20f;
}
} catch (InterruptedException e) {
}
}
else
if(angle>260f)
{
angle=-20f;
}
else if(angle<-20f)
{
angle=260f;
}
else{
c.rotate(angle,getWidth()/2,getHeight()/2);
}
super.onDraw(c);
}
}
You may set a fixed angle and use postDelayed to clear it after 2 seconds.
public class RotatoryKnobView extends ImageView {
private float angle = -20f;
private float theta_old=0f;
private RotaryKnobListener listener;
private Float fixedAngle;
private float settleAngle;
private Runnable unsetFixedAngle = new Runnable() {
#Override
public void run() {
angle = settleAngle;
fixedAngle = null;
invalidate();
}
};
public interface RotaryKnobListener {
public void onKnobChanged(float arg);
}
public void setKnobListener(RotaryKnobListener l )
{
listener = l;
}
public RotatoryKnobView(Context context) {
super(context);
initialize();
}
public RotatoryKnobView(Context context, AttributeSet attrs)
{
super(context, attrs);
initialize();
}
public RotatoryKnobView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
initialize();
}
private float getTheta(float x, float y)
{
float sx = x - (getWidth() / 2.0f);
float sy = y - (getHeight() / 2.0f);
float length = (float)Math.sqrt( sx*sx + sy*sy);
float nx = sx / length;
float ny = sy / length;
float theta = (float)Math.atan2( ny, nx );
final float rad2deg = (float)(180.0/Math.PI);
float thetaDeg = theta*rad2deg;
return (thetaDeg < 0) ? thetaDeg + 360.0f : thetaDeg;
}
public void initialize()
{
this.setImageResource(R.drawable.rotoron);
setOnTouchListener(new OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event) {
float x = event.getX(0);
float y = event.getY(0);
float theta = getTheta(x,y);
switch(event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_POINTER_DOWN:
theta_old = theta;
break;
case MotionEvent.ACTION_MOVE:
invalidate();
float delta_theta = theta - theta_old;
theta_old = theta;
int direction = (delta_theta > 0) ? 1 : -1;
angle += 5*direction;
notifyListener(angle+20);
break;
}
return true;
}
});
}
private void notifyListener(float arg)
{
if (null!=listener)
listener.onKnobChanged(arg);
}
void setFixedAngle(float angle, float settleAngle) {
fixedAngle = angle;
this.settleAngle = settleAngle;
postDelayed(unsetFixedAngle, 2000);
}
protected void onDraw(Canvas c)
{
if(fixedAngle==null) {
if (angle > 270) {
setFixedAngle(270, -15);
} else if (angle < -20f) {
setFixedAngle(-20, 260);
}
}
Log.d("angle", "angle: " + angle + " fixed angle: " + fixedAngle);
c.rotate(fixedAngle == null ? angle : fixedAngle,getWidth()/2,getHeight()/2);
super.onDraw(c);
}
}
`
I think the ultimate answer here is to implement your own class by extending SurfaceView and then overriding onDraw( Canvas canvas )
You can then use the Canvas routines to render your control.
There are a lot of good examples out there if you google.
To get started initialize the surface view:
// So things actually render
setDrawingCacheEnabled(true);
setWillNotDraw(false);
setZOrderOnTop(true);
// Controls the drawing thread.
getHolder().addCallback(new CallbackSurfaceView());
Override onDraw and add your rendering routines. You can layer them
as you go.
public void onDraw(Canvas canvas) {
// Always Draw
super.onDraw(canvas);
drawBackground(canvas);
drawKnobIndentWell(canvas);
drawKnob(canvas);
drawKnobLED( canvas ); //etc....
}
An example of a Callback and an update thread:
/**
* This is the drawing callback.
* It handles the creation and destruction of the drawing thread when the
* surface for drawing is created and destroyed.
*/
class CallbackSurfaceView implements SurfaceHolder.Callback {
Thread threadIndeterminant;
RunnableProgressUpdater runnableUpdater;
boolean done = false;
/**
* Kills the running thread.
*/
public void done() {
done = true;
if (null != runnableUpdater) {
runnableUpdater.done();
}
}
/**
* Causes the UI to render once.
*/
public void needRedraw() {
if (runnableUpdater != null) {
runnableUpdater.needRedraw();
}
}
/**
* When the surface is created start the drawing thread.
* #param holder
*/
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (!done) {
threadIndeterminant = new Thread(runnableUpdater = new RunnableProgressUpdater());
threadIndeterminant.start();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
/**
* When the surface is destroyed stop the drawing thread.
* #param holder
*/
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (null != runnableUpdater) {
runnableUpdater.done();
threadIndeterminant = null;
runnableUpdater = null;
}
}
}
/**
* This is the runnable for the drawing operations. It is started and stopped by the callback class.
*/
class RunnableProgressUpdater implements Runnable {
boolean surfaceExists = true;
boolean needRedraw = false;
public void done() {
surfaceExists = false;
}
public void needRedraw() {
needRedraw = true;
}
#Override
public void run() {
canvasDrawAndPost();
while (surfaceExists) {
// Renders continuously during a download operation.
// Otherwise only renders when requested.
// Necessary so that progress bar and cirlce activity update.
if (syncContext.isRunning()) {
canvasDrawAndPost();
needRedraw = true;
} else if (needRedraw) {
canvasDrawAndPost();
needRedraw = false;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Don't care
}
}
// One final update
canvasDrawAndPost();
}
/**
* Routine the redraws the controls on each loop.
*/
private synchronized void canvasDrawAndPost() {
Canvas canvas = getHolder().lockCanvas();
if (canvas != null) {
try {
draw(canvas);
} finally {
getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
If you decide to go this route you can customize your control from XML using
custom values.
<com.killerknob.graphics.MultimeterVolumeControl
android:id="#+id/volume_control"
android:layout_below="#id/divider_one"
android:background="#android:color/white"
android:layout_width="match_parent"
android:layout_height="60dp"
android:minHeight="60dp"
custom:ledShadow="#357BBB"
custom:ledColor="#357BBB"
custom:knobBackground="#color/gray_level_13"
custom:knobColor="#android:color/black"
/>
When you create a custom control you reference it by its package name.
You create custom variable in a resource file under /values and then reference
them in your class.
More details here:
http://developer.android.com/training/custom-views/create-view.html
This may be more work then you want to do, but I think you will end up with a more professional looking control and the animations will be smoother.
At any rate, looks like a fun project. Good Luck.
Hi I'm trying to perform Translate, Scale and Rotate on View (FrameLayout) in android.
In brief, I've a Fresco's SimpleDraweeView inside FrameLayout, as Fresco is not supporting Matrix transformations, so as an alternative I put that in FrameLayout and doing Translation, Rotation and Scaling.
I've extended FrameLayout here..
public class InteractiveFrameLayout extends FrameLayout {
private ViewTransformer mViewTransformer;
public InteractiveFrameLayout(Context context) {
super(context);
init(context);
}
public InteractiveFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public InteractiveFrameLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
// Determine dimensions of 'earth' image
int baseViewWidth = (int) getResources().getDimension(R.dimen.animation_image_size);
int baseViewHeight = (int) getResources().getDimension(R.dimen.animation_image_size);
// Setup Gesture Detectors
mViewTransformer = new ViewTransformer(this, baseViewWidth, baseViewHeight);
}
public boolean onTouchEvent(MotionEvent event) {
return mViewTransformer.onTouchEvent(event) || super.onTouchEvent(event);
}
}
Below class takes view and does everything related to ViewTransformations.
public class ViewTransformer {
private View mView;
private Vector2D position;
private float scale = 1;
private float angle = 0;
private TouchManager touchManager = new TouchManager(2);
public ViewTransformer(View view, int viewWidth, int viewHeight) {
mView = view;
position = new Vector2D();
position.set(viewWidth / 2, viewHeight / 2);
}
public boolean onTouchEvent(MotionEvent event) {
try {
touchManager.update(event);
if (touchManager.getPressCount() == 1) {
position.add(touchManager.moveDelta(0));
ViewAffineOperation.moveViewTo(mView, position.getX(), position.getY());
}
else {
if (touchManager.getPressCount() == 2) {
Vector2D current = touchManager.getVector(0, 1);
Vector2D previous = touchManager.getPreviousVector(0, 1);
float currentDistance = current.getLength();
float previousDistance = previous.getLength();
if (previousDistance != 0) {
scale *= currentDistance / previousDistance;
ViewAffineOperation.scaleViewBy(mView, scale);
}
angle -= Vector2D.getSignedAngleBetween(current, previous);
ViewAffineOperation.rotateViewBy(mView, getDegreesFromRadians(angle));
}
}
mView.invalidate();
}
catch(Throwable t) {
// So lazy...
}
return true;
}
private static float getDegreesFromRadians(float angle) {
return (float)(angle * 180.0 / Math.PI);
}
public float getScale() {
return scale;
}
public float getRotationDegrees() {
return angle;
}
}
and this one
public class ViewAffineOperation {
public static void moveViewTo(View view, float focusX, float focusY) {
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
int midPointX = layoutParams.width >> 1;
int midPointY = layoutParams.height >> 1;
float dx = (focusX - midPointX);
float dy = (focusY - midPointY);
view.setTranslationX(view.getTranslationX() + dx);
view.setTranslationY(view.getTranslationY() + dy);
}
public static void scaleViewBy(View view, float scaleFactor) {
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
}
public static void rotateViewBy(View view, float degrees) {
view.setRotation(degrees);
}
}
The Key problem is in
view.setTranslationX(view.getTranslationX() + dx);
view.setTranslationY(view.getTranslationY() + dy);
This one is most promising thing what I've found in stack over flow.. Rotate and scale a view based on one handle in Android
Thanks a ton in Advance.
#Sasha Salauyou I'm trying to reach you to help me in finding solution.
I have the following situation:
ZoomAndPanLayout
|
+---> ImageView
|
+---> FrameLayout (DragLayer)
|
+---> One or more controls. A view with a circle drawn on it.
With some minor issues because I don't care for now about screen bound the ZoomAndPanLayout works. I implemented ZoomAndPan like this:
public class ZoomAndPanLayout extends FrameLayout {
//region Constants
public static final float DEFAULT_MIN_SCALE_FACTOR = 1.0f;
public static final float DEFAULT_MAX_SCALE_FACTOR = 5.0f;
// endregion Constants
// region Fields
private float translationX = 0;
private float translationY = 0;
private float pivotX = 0;
private float pivotY = 0;
private float oldX;
private float oldY;
private float scaleFactor = 1.0f;
private float minScaleFactor = ZoomAndPanLayout.DEFAULT_MIN_SCALE_FACTOR;
private float maxScaleFactor = ZoomAndPanLayout.DEFAULT_MAX_SCALE_FACTOR;
private ScaleGestureDetector scaleGestureDetector = null;
// endregion Fields
// region Constructor
public ZoomAndPanLayout(Context context) {
super(context);
this.initialize(context);
}
public ZoomAndPanLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.initialize(context);
}
public ZoomAndPanLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.initialize(context);
}
private void initialize(Context context) {
this.scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener());
}
// endregion Constructor
#Override
public boolean onTouchEvent(MotionEvent event) {
this.scaleGestureDetector.onTouchEvent(event);
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
{
this.oldX = event.getX();
this.oldY = event.getY();
break;
}
case MotionEvent.ACTION_MOVE:
{
if (!this.scaleGestureDetector.isInProgress())
{
float x = event.getX();
float y = event.getY();
float deltaX = x - this.oldX;
float deltaY = y - this.oldY;
this.translationX += deltaX;
this.translationY += deltaY;
this.applyTransformations();
this.oldX = x;
this.oldY = y;
}
}
}
return true;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return this.scaleGestureDetector.onTouchEvent(event);
}
private void applyTransformations() {
final View child = this.getChildAt(0);
if (child != null)
{
child.setPivotX(this.pivotX);
child.setPivotY(this.pivotY);
child.setScaleX(this.scaleFactor);
child.setScaleY(this.scaleFactor);
// TODO: bound child to screen limits
child.setTranslationX(this.translationX);
child.setTranslationY(this.translationY);
}
}
public Rect getChildRect() {
View child = this.getChildAt(0);
if (child != null)
{
Rect outRect = new Rect();
outRect.right = (int) (child.getWidth() * child.getScaleX());
outRect.bottom = (int) (child.getHeight() * child.getScaleY());
int[] location = new int[2];
child.getLocationOnScreen(location);
outRect.offset(location[0], location[1]);
return outRect;
}
else
{
return new Rect(0, 0, 0, 0);
}
}
// region Private Inner Enums, Interfaces and Classes
private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
private ZoomAndPanLayout upper = ZoomAndPanLayout.this;
#Override
public boolean onScale(ScaleGestureDetector detector) {
float newScaleFactor = detector.getScaleFactor();
float originalScaleFactor = upper.scaleFactor;
upper.scaleFactor *= newScaleFactor;
// Bound the scaleFactor to the min and max limits
if (upper.scaleFactor >= upper.maxScaleFactor)
{
upper.scaleFactor = upper.maxScaleFactor;
newScaleFactor = upper.maxScaleFactor / originalScaleFactor;
}
else if (upper.scaleFactor * newScaleFactor <= upper.minScaleFactor)
{
upper.scaleFactor = upper.minScaleFactor;
newScaleFactor = upper.minScaleFactor / originalScaleFactor;
}
// set pivot
View child = upper.getChildAt(0);
if (child != null)
{
if (newScaleFactor * child.getWidth() * upper.scaleFactor <= originalScaleFactor * child.getWidth()
|| newScaleFactor * child.getHeight() * upper.scaleFactor <= originalScaleFactor * child.getWidth())
{
upper.pivotX = newScaleFactor * child.getWidth() / 2;
upper.pivotY = newScaleFactor * child.getHeight() / 2;
}
else
{
upper.pivotX = detector.getFocusX();
upper.pivotY = detector.getFocusY();
}
}
upper.applyTransformations();
return true;
}
}
// endregion Private Inner Enums, Interfaces and Classes
}
When I create each child of DragLayer I assign to them a OnLongClickListener, but the god damn thing dose not fire. when I long click on any child of DragLayer.
Any idea how can I implement this using both my idea for ZoomAndPanLayout or any idea. If you ask yourself why I need ZoomAndPanLayout, it is because I must be able to zoom and pan any layout not just an ImageView.
Any idea?
Probably because you return true in onTouchEvent method.
When true is returned it consumed an event and children don't receive their own onTouchEvent.
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return this.scaleGestureDetector.onTouchEvent(event);
}
always return true and it also blocks children from receiving an event.
Have a ListView and when the activity starts I want it to scroll from the bottom to the top. I can get this working by setting myListView.setStackFromBottom(true) in onCreate so the list is at the bottom when the activity loads.
Then I override onWindowFocusChanged and use smoothScrollToPosition(0) which will scroll the list to the top. However, I need the scroll speed to gradually slow down as it comes to the top, similar to what a fling looks like. Is there any way to do this with an animation or another way?
Thanks.
You can write your own scroller by using CountDownTimer.
import android.content.Context;
import android.os.CountDownTimer;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.animation.AnticipateOvershootInterpolator;
import android.view.animation.Interpolator;
import android.widget.ScrollView;
public class SmoothScrollView extends ScrollView {
private static final long SCROLL_DURATION = 1500; //milliseconds
//interpolator for scroller
private static final Interpolator INTERPOLATOR = new AnticipateOvershootInterpolator(1);
private SmoothScroller smoothScroller;
public SmoothScrollView(Context context) {
this(context, null, 0);
}
public SmoothScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SmoothScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setSmoothScrollingEnabled(true);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (smoothScroller != null)//we are scrolling
return true;
else return super.onTouchEvent(ev);
}
#Override
public boolean executeKeyEvent(KeyEvent ev) {
if (smoothScroller != null)//we are scrolling
return true;
else return super.executeKeyEvent(ev);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (smoothScroller != null)//we are scrolling
return true;
else return super.onInterceptTouchEvent(ev);
}
public void smoothScrollTo(int scrollX, int scrollY) {
if (smoothScroller != null) {
smoothScroller.cancel();
}
int deltaY = scrollY - getScrollY();
int deltaX = scrollX - getScrollX();
smoothScroller = new SmoothScroller(SCROLL_DURATION, getScrollX(), getScrollY(), deltaX, deltaY);
smoothScroller.start();
}
private class SmoothScroller extends CountDownTimer {
private int fromX;
private int fromY;
private int deltaX;
private int deltaY;
private float scrollTime;
public SmoothScroller(long scrollTime, int fromX, int fromY, int deltaX, int deltaY) {
super(scrollTime, 1);
this.scrollTime = scrollTime;
this.fromX = fromX;
this.fromY = fromY;
this.deltaX = deltaX;
this.deltaY = deltaY;
}
#Override
public void onTick(long millisUntilFinished) {
float delta = (scrollTime - millisUntilFinished) / scrollTime;
delta = INTERPOLATOR.getInterpolation(delta);
int x = fromX + ((int) (delta * deltaX));
int y = fromY + ((int) (delta * deltaY));
smoothScrollTo(x, y);
}
#Override
public void onFinish() {
float delta = 1f;
int x = fromX + ((int) (delta * deltaX));
int y = fromY + ((int) (delta * deltaY));
smoothScroller = null;
scrollTo(x, y);
}
}
}
This is what I use. Just change the INTERPOLATOR and SCROLL_DURATION based on your needs and call smoothScrollTo instead scrollTo.
I am pretty sure changing ScrollView to ListView won`t cause any problems.