How to make a linear layout scrollable and zoomable in Android? - android

I have a LinearLayout in my Android app and I would like to make it scrollable in all directions. I simply implemented this with a HorizontalScrollView and ScrollView under which I placed my layout.
This works great, but I would like to implement pinch-zoom as well.
So I was inspired by this question: Android - zoom in/out RelativeLayout with spread/pinch
and I came up with the following layout:
<com.example.ZoomableScrollView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0.2"
android:scrollbars="none"
android:background="#android:color/black">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<LinearLayout
android:id="#+id/sheet_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
</LinearLayout>
</HorizontalScrollView>
</com.example.ZoomableScrollView>
The children of the inner LinearLayout are dynamically added (they are simple TextViews inside horizontal LinearLayouts to make a simple table).
The ZoomableScrollView is quite simple and looks like this:
public class ZoomableScrollView extends ScrollView {
float mScaleFactor = 1.f;
private final ScaleGestureDetector mScaleDetector;
static final float MIN_SCALE = 1f;
static final float MAX_SCALE = 4f;
public ZoomableScrollView(Context context) {
this(context, null, 0);
}
public ZoomableScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ZoomableScrollView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
mScaleDetector = new ScaleGestureDetector(context, new OnScaleListener());
}
#Override
public boolean onTouchEvent(MotionEvent event) {
Log.w("DEBUG", "On touch event");
boolean retVal = mScaleDetector.onTouchEvent(event);
return super.onTouchEvent(event) || retVal;
}
#Override
protected void dispatchDraw(Canvas canvas) {
Log.w("DEBUG", String.format("Scaling with scale factor %f", mScaleFactor));
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
private class OnScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener
{
#Override
public boolean onScale(ScaleGestureDetector detector)
{
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(MIN_SCALE, Math.min(mScaleFactor, MAX_SCALE));
Log.w("DEBUG", "OnScale: scale factor: " + mScaleFactor);
invalidate();
return true;
}
}
}
I have 2 issues with this:
The zooming is not very smooth, sometimes the View will scroll when it should zoom. That's not such a big deal atm.
When zooming, the LinearLayout becomes smaller and you can't scroll everywhere anymore. I think I understand why this is the case, but I don't know how to change that and I'm thinking that I went into the wrong direction here.
I don't mind changing the layout completely. In the end, I would like to have it behave a bit like a browser, where you can zoom and scroll everywhere (not as complex though). Is there a simple solution for this ?

As for two dimensional scrollView I had used this one. Maybe you should try to add pinch for it.

Related

Override Button to draw text upside down

How could I go about overriding the Button class to draw the text upside down? The formatting of other libraries I'm using screw up when the button itself it rotated, but I need the text flipped.
I'm not familiar with the onDraw method, and I'm not sure how I could make a subclass for this.
Any help is appreciated, thanks!
You can use the rotation attribute in your button something like this:
<Button
android:id="#+id/test_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="text button"
android:rotation="180"/>
Or if you prefer using a custom button, you can create it something like this:
public class FlippedTextButton extends Button {
public FlippedTextButton(Context context) {
super(context);
}
public FlippedTextButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FlippedTextButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
float y = this.getHeight() / 2.0f;
float x = this.getWidth() / 2.0f;
canvas.rotate(180, x, y);
super.onDraw(canvas);
canvas.restore();
}
}
Then you can use it with:
<!-- Change to your class package name. -->
<com.example.flippedtext.FlippedTextButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Flipped text"/>
Here is the sample project on github: https://github.com/isnotmenow/FlippedText

Analyse and handle scroll gestures in Imageview (with canvas) without a ScrollView

I've tried many things to handle my problem, but it didn't really work and google also couldn't help me.
I've an ImageView which contains a canvas. The canvas draws a part of a graph. So when the user scrolls (horizontally) the canvas should draw another part of the graph. For example:
The canvas width is 200px
The canvas draws the graph from x=0 to x=200
The user scrolls (horizontally), he moves his finger over 100px
The canvas should now draw the graph from x=100 to x=300
The canvas should redraw by every pixel the user scrolls (for a smooth scrolling)
This solution did not work:
Draw the whole graph and put it in a Scrollview. This delivers a OutOfMemoryError, because the canvas/bitmap is too big.
Also I need to do some other things, so I need to do this like I described above.
Here is the Code:
XML:
<de.touristenfahrerforum.Marcel.Fragments.SpeedGraph
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="#+id/graph"
android:background="#android:color/black"
android:name="de.touristenfahrerforum.MarcelMoiser.Fragments.SpeedGraph">
</de.touristenfahrerforum.Marcel.Fragments.SpeedGraph>
Java Class:
public class SpeedGraph extends ImageView
{
...
public SpeedGraph(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public SpeedGraph(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SpeedGraph(Context context) {
super(context);
}
public void setup( ArrayList<Location> locations, ScrollViewListener scrollViewListener )
{
...
Bitmap bitmap = Bitmap.createBitmap(speedCanvasWidth,speedCanvasHeight,Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
this.setImageBitmap(bitmap);
...}
...
private void drawGraph(int start, int end, int color)
{
graphPaint.setColor(color);
long startTime = locations.get(start).getTime();
Path path = new Path();
path.moveTo((locations.get(start).getTime()-startTime)/10*V.LOGICAL_DENSITY, speedCanvasHeight-(int)(locations.get(start).getSpeed()*3.6*SIZE_FACTOR));
for( int it = start; it < end; it++ )
{
Location location = locations.get(it);
path.lineTo((location.getTime()-startTime)/10*V.LOGICAL_DENSITY, speedCanvasHeight-(int)(location.getSpeed()*3.6*SIZE_FACTOR));
}
canvas.drawPath(path,graphPaint);
}
...
So I would like to implement something that recognizes horizontal scroll gestures and gives me a number of pixels that shows me the pixels the user has scrolled.
Thank you guys in advance
It's the first time that nobody answered, but i figured out how to solve my problem.
The best way is to implement GestureDetector.OnGestureListener and overwrite all it's methods. Also you have to create a GestureDetector Object. The method OnScroll(...) is where to look for the pixels the user has scrolled.
Here is some code:
public class SpeedGraph extends ImageView implements GestureDetector.OnGestureListener
{
private Context context;
...
public void setup( ... )
{
this.gestureDetector = new GestureDetector(context, this);
gestureDetector.setIsLongpressEnabled(false);
...}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
{
scrolled+=distanceX;
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
drawGrid();
redrawGraphsInCanvas();
this.invalidate();
return true;
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev){
gestureDetector.onTouchEvent(ev);
return true;
}
...

Paralax effect in app background

Im new in Android world. I want to put some parallax background effects in my app.
How can I do it? How to approach to this in Android?
Is there any productive way to create 2-3 layer parallax background? Is there some tool, or class in android API?
Or maybe I have to modify background image location or margins "manually" in code?
Im using API level 19.
I have tried to understand Paralloid library, but this is too big to understand without any explanation. Im new to Android and Java, im not familiar with all Layouts and other UI objects, however I'm familiar with MVC.
I started bounty, maybe someone can explain step by step how that library works.
This is what you can do:
In your activity/fragment layout file specify 2 ScrollView's (say background_sv and content_sv).
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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" >
<com.example.parallax.MyScrollView
android:id="#+id/background_sv"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/parallax_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="..." />
</com.example.parallax.MyScrollView>
<com.example.parallax.MyScrollView
android:id="#+id/content_sv"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</com.example.parallax.MyScrollView>
</RelativeLayout>
Add a dummy view in the content scrollview of the height of the background and make it transparent. Now, attach a scroll listener to the content_sv. When the content scrollview is scrolled, call
mBgScrollView.scrollTo(0, (int)(y /*scroll Of content_sv*/ / 2f));
The existing API's doesn't have the support to get the scroll events.
Hence, we need to create a Custom ScrollView, to provide the ScrollViewListener.
package com.example.parallax;
// imports;
public class MyScrollView extends ScrollView {
public interface ScrollViewListener {
void onScrollChanged(MyScrollView scrollView, int x, int y, int oldx, int oldy);
}
private ScrollViewListener scrollViewListener = null;
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScrollViewListener(ScrollViewListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if(scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
}
}
}
Here is the activity which hosts both the content ScrollView and background ScrollView
package com.example.parallax;
// imports;
public class ParallaxActivity extends Activity implements ScrollViewListener {
private MyScrollView mBgScrollView;
private MyScrollView mContentScrollView;
#Override
public void onCreate(Bundle savedInstanceState) {
mBgScrollView = findViewById(R.id.background_sv);
mContentScrollView = findViewById(R.id.content_sv);
mContentScrollView.setOnScrollListener(this);
}
// this is method for onScrollListener put values according to your need
#Override
public void onScrollChanged(MyScrollView scrollView, int x, int y, int oldx, int oldy) {
super.onScrollChanged(scrollView, x, y, oldx, oldy);
// when the content scrollview will scroll by say 100px,
// the background scrollview will scroll by 50px. It will
// look like a parallax effect where the background is
// scrolling with a different speed then the content scrollview.
mBgScrollView.scrollTo(0, (int)(y / 2f));
}
}
I think the question is unclear, so this is not really an answer so much as an attempt to clarify with more detail than I could include in a comment.
My question is about what kind of parallax effect you want to achieve. Given these three examples (they are demo apps you can install from the Play Store), which if any has the type of parallax effect you want? Please answer in a comment.
Paralloid Demo
Parallax Scroll Demo
Google IO App
Given an answer, we all will find it easier to help out. If you edit your question to include this information, it will be improved.
The following contains an example application published by the author of Paralloid:
https://github.com/chrisjenx/Paralloid/tree/master/paralloidexample
From the GitHub page under the 'Getting Started' section:
Layout
ScrollView
This is an example, please refer to the paralloidexample App for full
code.
<FrameLayout ..>
<FrameLayout
android:id="#+id/top_content"
android:layout_width="match_parent"
android:layout_height="192dp"/>
<uk.co.chrisjenx.paralloid.views.ParallaxScrollView
android:id="#+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:id="#+id/scroll_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="192dp"/>
</uk.co.chrisjenx.paralloid.views.ParallaxScrollView>
</FrameLayout>
Fragment
Inside your onViewCreated() or onCreateView().
//...
FrameLayout topContent = (FrameLayout) rootView.findViewById(R.id.top_content);
ScrollView scrollView = (ScrollView) rootView.findViewById(R.id.scroll_view);
if (scrollView instanceof Parallaxor) {
((Parallaxor) scrollView).parallaxViewBy(topContent, 0.5f);
}
// TODO: add content to top/scroll content
Thats it!
Have a look at the Parallaxor interface for applicable Parallax
methods.
Hope this helps!
Also, here is a link to Google's 'getting started' page for android.
Also, here is a link to a 'java tutorial for complete beginners'.
As well as link to some documentation about layouts, which 'define the visual structure for a user interface'.
That being said, you would use the layout to define what the interface looks like and use the subsequent example code to define what happens when you interact with it.
P.S. You can see the application in action here
I use the ParallaxScroll library. Very easy to use, good samples and well documented.
Here is how it can be done using ScrollView and it's background image. I've committed the code in github.
You need to extend the ScrollView and Drawable classes.
By default the ScrollView background height will be same as viewport height. To achieve the parallax effect, the background height should be larger and should be based on the ScrollView child height and the background scrolling factor we want to impose.
Background scroll factor of 1 indicates, background height is same as ScrollView child height and hence background will scroll with same offset as the child scrolls.
0.5 indicates, background height is 0.5 times ScrollView child extended height and will scroll 50% slower compared to the child contents. This effectively brings the parallax scrolling effect.
Call following method from ScrollView constructor:
void init() {
// Calculate background drawable size before first draw of scrollview
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
// Remove the listener
getViewTreeObserver().removeOnPreDrawListener(this);
mDrawable = (ParallaxDrawable) getBackground();
if(mDrawable != null && mDrawable instanceof ParallaxDrawable) {
// Get the only child of scrollview
View child = getChildAt(0);
int width = child.getWidth();
// calculate height of background based on child height and scroll factor
int height = (int) (getHeight() + (child.getHeight() - getHeight()) * mScrollFactor);
mDrawable.setSize(width, height);
}
return true;
}
});
}
When ScrollView is scrolled, take into consideration the scroll offset while drawing the background. This basically achieves the parallax effect.
ParallaxScrollView:
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
if(mDrawable != null && mDrawable instanceof ParallaxDrawable) {
// set the scroll offset for the background drawable.
mDrawable.setScrollOffset(x*mScrollFactor, y*mScrollFactor);
}
}
ParallaxDrawable:
#Override
public void draw(Canvas canvas) {
// To move the background up, translate canvas by negative offset
canvas.translate(-mScrollXOffset, -mScrollYOffset);
mDrawable.draw(canvas);
canvas.translate(mScrollXOffset, mScrollYOffset);
}
protected void onBoundsChange(Rect bounds) {
// This sets the size of background drawable.
mDrawable.setBounds(new Rect(bounds.top, bounds.left, bounds.left + mWidth, bounds.top + mHeight));
}
Usage of ParallaxScrollView and ParallaxDrawable:
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.parallax_layout);
final ParallaxScrollView scrollView = (ParallaxScrollView) findViewById(R.id.sv);
ParallaxDrawable drawable = new ParallaxDrawable(getResources().getDrawable(R.drawable.bg));
scrollView.setBackground( drawable, 0.2f );
}
}
parallax_layout.xml:
<manish.com.parallax.ParallaxScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/sv"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#fff"
android:text="#string/text" />
<View
android:layout_width="match_parent"
android:layout_height="5dp" />
...
</LinearLayout>
</manish.com.parallax.ParallaxScrollView>
The Android API does not support much concrete tools for it as you probably noticed. In API 20 they added elevation which is an attribute for depth. This does not support parallax layouts itself but I would say it's a step by Google to make this kind of work easier. If you want a wild guess on if and when, I would say that parallax utilities could be added before API 25 is released, based on the latest update and the progress in battery efficiency.
For now all you need is to listen for some kind of movement and change the views positions based on a value representing elevation.
Your question made me upgrade my own project and this is how I did it using ViewDragHelper inside a Fragment.
public class MainFragment extends Fragment implements View.OnTouchListener {
private ImageView mDecor, mBamboo, mBackgroundBamboo;
private RelativeLayout mRootLayout;
private ViewDragHelper mDragHelper;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mRootLayout = (RelativeLayout) inflater.inflate(R.layout.fragment_main, container, false);
mRootLayout.setOnTouchListener(this);
mDecor = (ImageView) mRootLayout.findViewById(R.id.decor);
mBamboo = (ImageView) mRootLayout.findViewById(R.id.bamboo);
mBackgroundBamboo = (ImageView) mRootLayout.findViewById(R.id.backround_bamboo);
mDragHelper = ViewDragHelper.create(mRootLayout, 1.0f, new ViewDragHelper.Callback() {
private final float MAX_LEFT = -0;
private final float MAX_TOP = -20;
private final float MAX_RIGHT = 50;
private final float MAX_BOTTOM = 10;
private final float MULTIPLIER = 0.1f;
private final int DECOR_ELEVATION = 3;
private final int FRONT_BAMBOO_ELEVATION = 6;
private final int BACKGROUND_BAMBOO_ELEVATION = 1;
private float mLeft = 0;
private float mTop = 0;
#Override
public boolean tryCaptureView(View view, int i) {
return true;
}
#Override
public int clampViewPositionVertical(View child, int top, int dy) {
mTop += dy * MULTIPLIER;
mTop = mTop > MAX_BOTTOM ? MAX_BOTTOM : mTop < MAX_TOP ? MAX_TOP : mTop;
mDecor.setTranslationY(mTop * DECOR_ELEVATION);
mBamboo.setTranslationY(mTop * FRONT_BAMBOO_ELEVATION);
mBackgroundBamboo.setTranslationY(mTop * BACKGROUND_BAMBOO_ELEVATION);
return 0;
}
#Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
mLeft += dx * MULTIPLIER;
mLeft = mLeft < MAX_LEFT ? MAX_LEFT : mLeft > MAX_RIGHT ? MAX_RIGHT : mLeft;
mDecor.setTranslationX(mLeft * DECOR_ELEVATION);
mBamboo.setTranslationX(mLeft * FRONT_BAMBOO_ELEVATION);
mBackgroundBamboo.setTranslationX(mLeft * BACKGROUND_BAMBOO_ELEVATION);
return 0;
}
#Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy){
mRootLayout.requestLayout();
}
});
return mRootLayout;
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
mDragHelper.processTouchEvent(motionEvent);
// you can still use this touch listener for buttons etc.
return true;
}
}
Hi You can go with the below-given code for ParallaxView class
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.util.ArrayList;
public class ParallaxView extends SurfaceView implements Runnable {
private volatile boolean running;
private Thread gameThread = null;
// For drawing
private Paint paint;
private Canvas canvas;
private SurfaceHolder ourHolder;
// Holds a reference to the Activity
Context context;
// Control the fps
long fps =60;
// Screen resolution
int screenWidth;
int screenHeight;
ParallaxView(Context context, int screenWidth, int screenHeight) {
super(context);
this.context = context;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
// Initialize our drawing objects
ourHolder = getHolder();
paint = new Paint();
}
#Override
public void run() {
while (running) {
long startFrameTime = System.currentTimeMillis();
update();
draw();
// Calculate the fps this frame
long timeThisFrame = System.currentTimeMillis() - startFrameTime;
if (timeThisFrame >= 1) {
fps = 1000 / timeThisFrame;
}
}
}
private void update() {
// Update all the background positions
}
private void draw() {
if (ourHolder.getSurface().isValid()) {
//First we lock the area of memory we will be drawing to
canvas = ourHolder.lockCanvas();
//draw a background color
canvas.drawColor(Color.argb(255, 0, 3, 70));
// Draw the background parallax
// Draw the rest of the game
paint.setTextSize(60);
paint.setColor(Color.argb(255, 255, 255, 255));
canvas.drawText("I am a plane", 350, screenHeight / 100 * 5, paint);
paint.setTextSize(220);
canvas.drawText("I'm a train", 50, screenHeight / 100*80, paint);
// Draw the foreground parallax
// Unlock and draw the scene
ourHolder.unlockCanvasAndPost(canvas);
}
}
// Clean up our thread if the game is stopped
public void pause() {
running = false;
try {
gameThread.join();
} catch (InterruptedException e) {
// Error
}
}
// Make a new thread and start it
// Execution moves to our run method
public void resume() {
running = true;
gameThread = new Thread(this);
gameThread.start();
}
}// End of ParallaxView
To know more you can go **
here
**: http://gamecodeschool.com/android/coding-a-parallax-scrolling-background-for-android/

Pinch-Zoom Out does nothing, In makes screen disappear

I have to be missing something obvious, since everyone on the planet seems to be able to get the whole pinch-zoom thing to work. The main difference is most folks seem to be zooming on images, whereas I've a GridLayout (android-support-v7-gridlayout) with a bunch of text views. The goal seems simple enough: Allow the user to zoom in so that they can view a portion of the full grid at a larger size.
The grid draws fine, and horizontal & vertical scrolling work fine. And then, when I do a two-finger gesture with fingers moving apart (zoom), nothing happens, despite getting the log message that onScale has been called. When I move the two fingers in (pinch), all the views disappear (though my scroll bars are left).
I've looked at Matrix transformations but haven't seen that those work on anything but an image view.
The basic layout xml:
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<HorizontalScrollView
android:id="#+id/chooser_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</HorizontalScrollView>
</ScrollView>
Then in the activity
private ZoomGridLayout playerGrid;
and in onCreate:
ViewGroup gridParent = (ViewGroup) findViewById(R.id.chooser_layout);
playerGrid = new ZoomGridLayout(getBaseContext());
playerGrid.setRowCount(8);
playerGrid.setColumnCount(8);
LayoutParams layout =
new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
playerGrid.setDetector();
gridParent.addView(playerGrid, layout);
I then proceed to add ~150 small text views to the grid.
Here's the first take at ZoomGridLayout:
public class ZoomGridLayout extends GridLayout {
private final static String TAG = "ZGL";
private ScaleGestureDetector scaleDetector;
private Context context;
public void setDetector() {
scaleDetector = new ScaleGestureDetector(context, new ScaleGesture());
}
public ZoomGridLayout(Context c) {
super(c);
context = c;
}
#Override
public boolean onTouchEvent (MotionEvent event) {
scaleDetector.onTouchEvent(event);
return true;
}
private class ScaleGesture
extends ScaleGestureDetector.SimpleOnScaleGestureListener
{
#Override
public boolean onScale(ScaleGestureDetector detector) {
Log.v(TAG, detector.toString());
ScaleAnimation scale =
new ScaleAnimation(detector.getPreviousSpan(),
detector.getCurrentSpan(),
detector.getPreviousSpan(),
detector.getCurrentSpan());
scale.setFillAfter(true);
startAnimation(scale);
return true;
}
}
}
I've been beating my head against this for a couple days now. I'm new to Android programming, so not very familiar with the libraries, but I'm an experienced developer. Any pointers would be wonderful. TIA.
I gave up on animations (though eventually I'd like to try to make that work). Here's the new onScale method:
#Override
public boolean onScale(ScaleGestureDetector detector) {
Log.v(TAG, detector.toString());
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(MIN_SCALE, Math.min(mScaleFactor, MAX_SCALE));
invalidate();
return true;
}
this is then complemented by overriding the draw method:
#Override
public void dispatchDraw(Canvas canvas) {
String disp = String.valueOf(mScaleFactor);
Log.v(TAG, "dispatch draw " + disp);
canvas.save();
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
}
i also learned that since the parent is a ViewGroup instead of a View, that one needs to override the dispatch… methods instead of the on… methods.
there are so many different variants of this pattern out there. i hope this particular wrinkle helps someone else.

Rotating an image around a specified point doesn't work! (Android)

I'm rotating an ImageView with postRotate(float degrees, float px, float py), setting px and py to a few differnt values including (0,0) and (imgView.getHeight(),imgView.getWidth()) but it refuses to rotate around any other point than the center. I'm wondering whether it's got anything to do with the fact that my gravity is center in the LinearLayout?
My layout:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center">
<ImageView
android:id="#+id/imageTraj"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="matrix"
android:src="#drawable/impactangle" />
How I'm rotating the image:
matrix.postRotate(degrees,imageView.getHeight(),imageView.getWidth());
imageView.setImageBitmap(Bitmap.createBitmap(imageScaled, 0, 0,
imageScaled.getWidth(), imageScaled.getHeight(), matrix, true));
PS I've noticed there are a few similar questions, but none of them have suitable answers.
I am using a custom ImageView where I set the angle rotation.
public class CompassImage extends ImageView {
private float angleRotation;
public CompassImage(Context context) {
super(context);
}
public CompassImage(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CompassImage(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setAngleRotation(float angleRotation) {
this.angleRotation = angleRotation;
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
Rect clipBounds = canvas.getClipBounds();
canvas.save();
canvas.rotate(angleRotation, clipBounds.exactCenterX(), clipBounds.exactCenterY());
super.onDraw(canvas);
canvas.restore();
}
}
If you play around with clipBounds you may find that helpful.
A slight "hack", you could probably adjust the image size using something like Paint.net or Gimp, depending on your OS. That would make the image appear to spin on another point. This solution would be pointless if you are planning on using a lot of images though. This is a spinning cube tutorial using opengl.

Categories

Resources