Scroll ListView from bottom to top - android

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.

Related

Viewpager is not sliding properly in OnePlus 5T device

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);
}
}

Android: Select button closest when you remove your finger

I have multiple buttons in my LinearLayout on my activity_main, and want to detect which Button is closest to the users finger. When the users moves his finger in a single gesture the closest button should highlight and when he removes his finger the closest button should do its onClick function.
It should act like the default keyboard on android or the iphone calculator. It selects the button closest to the finger. When you drag your finger across it will change the selection to the closest key and only when you release your finger does it do the onClick function.
Referencing Get button coordinates and detect if finger is over them - Android
I got to the point where selection works, but only if I tap anywhere that isn't a button and it doesn't work to select closest button when not over a button.
(Programming for API 21 in case thats important)
activity_main.xml
<TextView/>
<ButtonLayout>
<LinearLayout1>
<Button1/>
<Space/>
<Button2/>
<Space/>
<Button3/>
<Space/>
<Button4/>
</LinearLayout1>
<LinearLayout2>
<Button5/>
<Space/>
<Button6/>
<Space/>
<Button7/>
<Space/>
<Button8/>
</LinearLayout2>
</OuterLinearLayout>
Java
private View.OnTouchListener buttonLayoutTouchListener= new View.OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
//OuterLayout
for (int i = 0; i < buttonLayout.getChildCount(); i++) {
View inner = buttonLayout.getChildAt(i);
//Inner
if(inner instanceof LinearLayout) {
for (int j = 0;j<((LinearLayout) inner).getChildCount();j++) {
View current = ((LinearLayout) inner).getChildAt(j);
if (current instanceof Button) {
Button b = (Button) current;
Rect rect = new Rect();
b.getGlobalVisibleRect(rect);
//factors for textview
rect.top-=300;
rect.bottom-=300;
if (!isPointWithin(x, y, rect.left, rect.right, rect.top,
rect.bottom)) {
b.getBackground().setState(defaultStates);
}
if (isPointWithin(x, y, rect.left, rect.right, rect.top,
rect.bottom)) {
if (b != mLastButton) {
mLastButton = b;
b.getBackground().setState(STATE_PRESSED);
//highlight button finger currently over
Log.d("button",mLastButton.getText().toString());
}
}
}
}
}
}
return true;
}
};
static boolean isPointWithin(int x, int y, int x1, int x2, int y1, int y2) {
return (x <= x2 && x >= x1 && y <= y2 && y >= y1);
}
Make a new class to extend LinearLayout and override onInterceptTouchEvent. Set your outer LinearLayout to the new class.
Warning: This has a side effect of calling onClick twice if you just tap a button (Once on parent, once on child). Here is my super dirty workaround. Please consider finding a real workaround and posting a reply.
Double Click workaround
//call if(doubleClick()) in buttons' onClicklistener
public mLastClickTime=0;
public boolean doubleClick() {
//29 is arbitrary
if (SystemClock.elapsedRealtime() - mLastClickTime < 29) {
return true;
}
mLastClickTime = SystemClock.elapsedRealtime();
return false;
}
ExtendedLinearLayout
import android.content.Context;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
public class ButtonLayout extends LinearLayout {
public int layoutTop;
public ButtonLayout(Context context) {
super(context);
}
public ButtonLayout(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public ButtonLayout(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ButtonLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Rect rect = new Rect();
this.getGlobalVisibleRect(rect);
layoutTop =rect.top;
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Button keep=null;
int x = (int) ev.getX();
int y = (int) ev.getY();
int count = 0;
//number of buttons
int buttonMax =0;
int distance = Integer.MAX_VALUE;
//Outer LinearLayout
outerLoop:
for (int i = 0; i < this.getChildCount(); i++) {
View inner = this.getChildAt(i);
if (inner instanceof LinearLayout) {
//Inner LinearLayout
for (int j = 0; j < ((LinearLayout) inner).getChildCount(); j++) {
View current = ((LinearLayout) inner).getChildAt(j);
if (current instanceof Button) {
buttonMax++;
Button b = (Button) current;
Rect rect = new Rect();
b.getGlobalVisibleRect(rect);
rect.top -= layoutTop;
rect.bottom -= layoutTop;
//finger in button
if (isPointWithin(x, y, rect.left, rect.right, rect.top,
rect.bottom)) {
b.setPressed(true);
keep=b;
break outerLoop;
}else{
b.setPressed(false);
count++;
int buttonDistance = distance(x, y, rect.left, rect.right, rect.top, rect.bottom);
if(buttonDistance<distance){
keep=b;
distance=buttonDistance;
}
}
}
}
}
}
//if non are selected let button be selected
if(count==buttonMax){
keep.setPressed(true);
}
//on release
if(ev.getAction()==MotionEvent.ACTION_UP){
keep.callOnClick();
return false;
}
return super.onInterceptTouchEvent(ev);
}
static boolean isPointWithin(int x, int y, int x1, int x2, int y1, int y2) {
return (x <= x2 && x >= x1 && y <= y2 && y >= y1);
}
static int distance(int x, int y,int x1, int x2, int y1, int y2){
x1 = Math.abs(x1-x);
x2 = Math.abs(x2-x);
y1 = Math.abs(y1-y);
y2 = Math.abs(y2-y);
x = (x1>x2?x2:x1);
y = (y1>y2?y2:y1);
return x+y;
}
}

OnClick listener not working on views in a CustomLayout extending ViewGroup

I have tried to implement the youtube like draggable panel using the Customlayout which extends ViewGroup. I have made use of the onTouchEvents to create and scaling on dragging effect.
Now, the problem is that the when dragged down, the selected item resides near the bottom of the screen as it happens in YouTube. When clicked on that item in that position, it should get back to the full screen mode. But to do so, I implemented the onClicklistener for the view but it simply doesn't respond.
Also, when at the bottom, how do I restrict it's motion in the vertical direction and allow it to move in the horizontal direction as it happens in YouTube.
Here is the CustomLayout class called DraggablePanel.
package visio.com.eventpage;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.Toast;
import static android.view.MotionEvent.INVALID_POINTER_ID;
/**
* Created by Rambo on 1/9/2018.
*/
public class DraggablePanel extends ViewGroup {
public final ViewDragHelper mDragHelper;
public View mHeaderView;
private View mDescView;
private float mInitialMotionX;
private float mInitialMotionY;
private int mDragRange;
private int mTop;
private float mDragOffset;
private Context context;
public int toponViewReleased;
public int screenWidth, screenHeight;
private String TAG = "DraggablePanel";
public void manualInvalidate(){
invalidate();
}
public DraggablePanel(Context context) {
this(context, null);
}
public DraggablePanel(Context context, AttributeSet attrs) {
this(context, attrs, 0);
this.context = context;
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
mHeaderView = findViewById(R.id.viewHeader);
mDescView = findViewById(R.id.viewDesc);
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
screenHeight = displayMetrics.heightPixels;
screenWidth = displayMetrics.widthPixels;
}
public DraggablePanel(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mDragHelper = ViewDragHelper.create(this, 1f, new DragHelperCallback());
}
private class DragHelperCallback extends ViewDragHelper.Callback {
#Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mHeaderView;
}
#Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
mTop = top;
float paddingOffset = (float)top/10;
mDragOffset = (float) top / mDragRange;
mHeaderView.setPivotX(mHeaderView.getWidth());
mHeaderView.setPivotY(mHeaderView.getHeight());
mHeaderView.setScaleX(1 - mDragOffset / 2);
mHeaderView.setScaleY(1 - mDragOffset / 2);
mHeaderView.setPadding(0,0,(int) paddingOffset, (int) paddingOffset);
mDescView.setAlpha(1 - mDragOffset);
if( top == 0){
}
requestLayout();
Log.d("onViewPositionChanged","Called");
}
#Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
int top = releasedChild.getTop()>screenHeight/3?screenHeight-150 - mHeaderView.getHeight():0;
Log.d("Heights","H1"+getHeight() + "child height" + releasedChild.getMeasuredHeight()+"top" +top);
mDragHelper.smoothSlideViewTo(releasedChild, releasedChild.getLeft(),top);
if( top!=0 ){
mDescView.setAlpha(0);
}
ViewCompat.postInvalidateOnAnimation(releasedChild);
toponViewReleased = top;
invalidate();
}
#Override
public int getViewVerticalDragRange(View child) {
return mDragRange;
}
#Override
public int clampViewPositionVertical(View child, int top, int dy) {
float descViewAlpha = mDescView.getAlpha();
final int topBound = getPaddingTop();
final int bottomBound = getHeight() - mHeaderView.getHeight() - 20;
int newTop = Math.min(Math.max(top, topBound), bottomBound);
if( descViewAlpha == 0 ){
newTop = 0;
}
Log.d(TAG,"clampVertical" + newTop);
return newTop;
}
#Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
final int rightBound = mHeaderView.getPaddingRight();
final int leftBound = screenWidth - mHeaderView.getWidth() - 20;
int newTop = Math.min(Math.max(left, rightBound), leftBound);
if(mDescView.getAlpha() != 0){
newTop = 0;
}
Log.d(TAG,"clampHorizontal" + newTop);
return newTop;
}
}
#Override
public void computeScroll() {
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getActionMasked();
if (( action != MotionEvent.ACTION_DOWN)) {
mDragHelper.cancel();
return super.onInterceptTouchEvent(ev);
}
final float x = ev.getX();
final float y = ev.getY();
boolean interceptTap = false;
switch (action) {
case MotionEvent.ACTION_DOWN: {
mInitialMotionX = x;
mInitialMotionY = y;
interceptTap = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
break;
}
case MotionEvent.ACTION_MOVE: {
final float adx = Math.abs(x - mInitialMotionX);
final float ady = Math.abs(y - mInitialMotionY);
final int slop = mDragHelper.getTouchSlop();
if (ady > slop && adx > ady) {
mDragHelper.cancel();
return false;
}
}
}
return mDragHelper.shouldInterceptTouchEvent(ev) || interceptTap;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
mDragHelper.processTouchEvent(ev);
final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY();
boolean isHeaderViewUnder = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
switch (action & ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mInitialMotionX = x;
mInitialMotionY = y;
break;
}
}
return isHeaderViewUnder && isViewHit(mHeaderView, (int) x, (int) y) || isViewHit(mDescView, (int) x, (int) y);
}
#Override
public boolean performClick() {
super.performClick();
if(toponViewReleased !=0){
mDragHelper.smoothSlideViewTo(mHeaderView, 0,0);
invalidate();
Log.d("DraggablePanel","Clicked top != 0");
}else{
Log.d("DraggablePanel","Clicked top == 0");
}
return true;
}
private boolean isViewHit(View view, int x, int y) {
int[] viewLocation = new int[2];
view.getLocationOnScreen(viewLocation);
int[] parentLocation = new int[2];
this.getLocationOnScreen(parentLocation);
int screenX = parentLocation[0] + x;
int screenY = parentLocation[1] + y;
return screenX >= viewLocation[0] && screenX < viewLocation[0] + view.getWidth() &&
screenY >= viewLocation[1] && screenY < viewLocation[1] + view.getHeight();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d(TAG,"onLayout Called");
mDragRange = getHeight() - mHeaderView.getHeight();
mHeaderView.layout(
0,
mTop,
r,
mTop + mHeaderView.getMeasuredHeight());
mDescView.layout(
0,
mTop + mHeaderView.getMeasuredHeight(),
r,
mTop + b);
}
}
Here is the Layout where the above class has been used.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerViewEvents"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<visio.com.eventpage.DraggablePanel
android:id="#+id/draggablePanel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:id="#+id/viewHeader"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginEnd="20dp"
android:gravity="center"
android:scaleType="fitXY"
android:src="#drawable/ic_event" />
<TextView
android:id="#+id/viewDesc"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF00FF"
android:gravity="center"
android:tag="desc"
android:text="Loreum Loreum"
android:textColor="#android:color/white"
android:textSize="35sp" />
</visio.com.eventpage.DraggablePanel>
</FrameLayout>

Android - View is moving out of screen post rotation

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.

How to achieve multitouch for whole activity?

I have one activity which is having linear layout and inside this layout there are several linear layouts and each linear layout is having set if buttons and text views. I want to achieve multi touch feature for whole screen means if user perform zoom in-zoom out using his finger then it should zoom in and zoom out whole screen(increase and decrease all buttons,text views size accordingly once).
How to achieve it using android 2.1?
regards,
Piks
This might give you an idea:
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
//import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
public class MultitouchView extends View {
private static final int STROKE_WIDTH = 1;
private static final int CIRCLE_RADIUS = 20;
private ArrayList<PointF> touchPoints = null;
private Paint drawingPaint = null;
private boolean isMultiTouch = false;
private int pathEffectPhase = 0;
public MultitouchView(Context context) {
super(context);
initialize(context);
}
public MultitouchView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize(context);
}
public MultitouchView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(touchPoints.size() > 0)
{
DashPathEffect effect = new DashPathEffect(new float[] {7,7}, pathEffectPhase);
PointF midpt = null;
drawingPaint.setPathEffect(effect);
for(int index=1; index<touchPoints.size(); ++index)
{
midpt = getMidPoint(
touchPoints.get(index - 1).x,touchPoints.get(index - 1).y,
touchPoints.get(index).x,touchPoints.get(index).y);
canvas.drawCircle(
touchPoints.get(index - 1).x,touchPoints.get(index - 1).y,
1, drawingPaint);
canvas.drawCircle(
touchPoints.get(index - 1).x,touchPoints.get(index - 1).y,
CIRCLE_RADIUS, drawingPaint);
canvas.drawCircle(touchPoints.get(index).x,touchPoints.get(index).y,
1, drawingPaint);
canvas.drawCircle(touchPoints.get(index).x,touchPoints.get(index).y,
CIRCLE_RADIUS, drawingPaint);
canvas.drawLine(
touchPoints.get(index - 1).x,touchPoints.get(index - 1).y,
touchPoints.get(index).x,touchPoints.get(index).y,
drawingPaint);
canvas.drawCircle(midpt.x,midpt.y, 10, drawingPaint);
}
++pathEffectPhase;
invalidate();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
int action = event.getAction() & MotionEvent.ACTION_MASK;
switch(action)
{
case MotionEvent.ACTION_DOWN:
{
invalidate();
break;
}
case MotionEvent.ACTION_POINTER_DOWN:
{
isMultiTouch = true;
setPoints(event);
invalidate();
break;
}
case MotionEvent.ACTION_POINTER_UP:
{
isMultiTouch = false;
break;
}
case MotionEvent.ACTION_MOVE:
{
if(isMultiTouch)
{
setPoints(event);
invalidate();
}
break;
}
}
return true;
}
private void initialize(Context context){
drawingPaint = new Paint();
drawingPaint.setColor(Color.RED);
drawingPaint.setStrokeWidth(STROKE_WIDTH);
drawingPaint.setStyle(Paint.Style.STROKE);
drawingPaint.setAntiAlias(true);
touchPoints = new ArrayList<PointF>();
}
public void setPoints(MotionEvent event){
touchPoints.clear();
int pointerIndex = 0;
for(int index=0; index<event.getPointerCount(); ++index)
{
pointerIndex = event.getPointerId(index);
touchPoints.add(new PointF(event.getX(pointerIndex),event.getY(pointerIndex)));
}
}
private PointF getMidPoint(float x1,float y1, float x2, float y2) {
PointF point = new PointF();
float x = x1 + x2;
float y = y1 + y2;
point.set(x / 2, y / 2);
return point;
}
}

Categories

Resources