Paralax effect in app background
Asked Answered
R

7

9

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.

Rochkind answered 4/10, 2014 at 13:52 Comment(7)
Maybe check out the following? github.com/chrisjenx/ParalloidSiphonophore
I don't understand how it works :( Lots of code, zero comments.Rochkind
Kamil, have revised my answer!Siphonophore
Also, which code in particular do you not understand?Siphonophore
You should add more details and a better explanation of what you want to achieve (eg: I have a viewpager/list/scroller/whatever...). If you have no much confidence with the sdk just try to explain from a user perspective. The key for a good answer is a detailed question.Hedberg
If you are new to android and java, I will suggest first do the basic stuff about views and animations, then jump to some advance stuff like this. If you are not familiar with layouts and UI stuff, get familiar with them otherwise it may be hard to understand :)Gatto
@Gatto Now I know that. I didn't realized that Android is so diffrent.Rochkind
S
13

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));
    }
}
Spook answered 8/10, 2014 at 20:38 Comment(6)
It looks simple and clean, but... there is no onScroll event in ScrollView.Rochkind
Ok. I think this is closest to simple and working parallax, but it is not complete. Before I reward you with bounty - please add information how to add onScrollChanged event. This works (Andy answer): #3949434Rochkind
Yes, we need to get the scroll values based on the onScrollChanged event. Need to create CustomScrollView like done in the link you've specified.Spook
Can you edit your answer and add some code? I don't want to reward incomplete answer with "go there and find out".Rochkind
Added all the data required to make this parallax effect.Spook
@SagarWaghmare you forgot to implement ScrollViewListener method.. already many times Kamil told you to add incomplete code.Necrophilia
S
4

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.

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.

Suppository answered 8/10, 2014 at 21:51 Comment(0)
S
2

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

Siphonophore answered 7/10, 2014 at 19:28 Comment(1)
Also, if you review the documentation and still have questions, maybe we should go from there!Siphonophore
M
1

I use the ParallaxScroll library. Very easy to use, good samples and well documented.

Mckay answered 7/10, 2014 at 17:54 Comment(4)
Well, I didn't knew that there is another free library. I will take a look at it, but don't think you will get +500 for 2 sentences :)Rochkind
It's a really nice library that's easy to use and I would appreciate if you awarded the bounty if it helped :)Mckay
@Rochkind i was wondering who will get the bounty if this answer works :3Gatto
@Gatto It's hard to choose. I made parallax after combining informations from 2 answers...Rochkind
O
1

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.

  1. 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;
        }       
    });     
}
  1. 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>
Ocelot answered 12/10, 2014 at 14:52 Comment(0)
C
0

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;
    }
}
Capitulate answered 8/10, 2014 at 18:9 Comment(0)
R
0

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 **

**: http://gamecodeschool.com/android/coding-a-parallax-scrolling-background-for-android/

Respirator answered 20/11, 2018 at 8:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.