Imageview parallax as you scroll - android

I saw many examples demonstrative parallax background as you scroll or listview parallax but I cannot find a clear example how to implement a parallax effect on images as you scroll in the activity.
An example implementation can be found in Airbnb app. As you scroll down you can see more of the image's bottom and as you scroll up you can see more of the image's top.
Any hints and tips on how to create such effect?

There are a few libraries that to a parallax effect, it depends on your app if they are useful for your particular case, for example:
ParallaxScroll
Paralloid
Google is your friend pal ;) if none of these suits your needs then you have to create a custom ScrollView but that's a longer story, first give them a try and post your results.
Edit
If none of these fit your requeriments then this is what you have to do:
First, create a custom ScrollView so you can listen to scroll changes.
public class ObservableScrollView extends ScrollView {
public interface OnScrollChangedListener {
public void onScrollChanged(int deltaX, int deltaY);
}
private OnScrollChangedListener mOnScrollChangedListener;
public ObservableScrollView(Context context) {
super(context);
}
public ObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if(mOnScrollChangedListener != null) {
mOnScrollChangedListener.onScrollChanged(l - oldl, t - oldt);
}
}
public void setOnScrollChangedListener(OnScrollChangedListener listener) {
mOnScrollChangedListener = listener;
}
}
Obviously you need to use this in your layout instead of the default ScrollView:
<your.app.package.ObservableScrollView
android:id="#+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
Also you need to wrap your ImageView inside a container to make the parallax work:
<FrameLayout
android:id="#+id/img_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
</FrameLayout>
Finally set your Activity as a listener for your brand new ObservableScrollView and let the parallax begin:
public class MyActivity extends Activity implements ObservableScrollView.OnScrollChangedListener {
private ObservableScrollView mScrollView;
private View imgContainer;
#Override
public void onCreate(Bundle savedInstanceState) {
// Init your layout and set your listener
mScrollView = (ObservableScrollView)findViewById(R.id.scroll_view);
mScrollView.setOnScrollChangedListener(this);
// Store the reference of your image container
imgContainer = findViewById(R.id.img_container);
}
#Override
public void onScrollChanged(int deltaX, int deltaY) {
int scrollY = mScrollView.getScrollY();
// Add parallax effect
imgContainer.setTranslationY(scrollY * 0.5f);
}
}
You can modify the 0.5 value depending on how much parallax you want.
Edit
The above answer works fine if your ImageView is in the top of the activity. I am posting some code below to add the functionality to have the ImageView anywhere in the activity layout which I successfully made to work. These are generic calculations (might have some mistakes) and with a little tweak you can have it working for your own case.
For this example I have a fix height for the image container 200dp and for the image 240dp. The main purpose is when the image container is in the middle of the screen have no parallax effect and as the user scroll up or down to apply the effect. So as the image container get closer to the top of the screen or closer to the bottom of the screen the more of the parallax effect will be applied. The following calculations are a little hard to understand by reading them so try to make an example with real numbers in paper.
public class MyActivity extends Activity implements ObservableScrollView.OnScrollChangedListener {
private ObservableScrollView mScrollView;
private View imgContainer;
private ImageView mImageView;
#Override
public void onCreate(Bundle savedInstanceState) {
// Init your layout and set your listener
mScrollView = (ObservableScrollView)findViewById(R.id.scroll_view);
mScrollView.setOnScrollChangedListener(this);
// Store the reference of your image container
imgContainer = findViewById(R.id.img_container);
// Store the reference of your image
mImageView = findViewById(R.id.img);
}
#Override
public void onScrollChanged(int deltaX, int deltaY) {
// Get scroll view screen bound
Rect scrollBounds = new Rect();
mScrollView.getHitRect(scrollBounds);
// Check if image container is visible in the screen
// so to apply the translation only when the container is visible to the user
if (imgContainer.getLocalVisibleRect(scrollBounds)) {
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics outMetrics = new DisplayMetrics ();
display.getMetrics(outMetrics);
// Get screen density
float density = getResources().getDisplayMetrics().density;
// Get screen height in pixels
float dpHeight = outMetrics.heightPixels / density;
int screen_height_pixels = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpHeight, getResources().getDisplayMetrics());
int half_screen_height = screen_height_pixels/2;
// Get image container height in pixels
int container_height_pixels = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics());
// Get the location that consider a vertical center for the image (where the translation should be zero)
int center = half_screen_height - (container_height_pixels/2);
// get the location (x,y) of the image container in pixels
int[] loc_screen = {0,0};
imgContainer.getLocationOnScreen(loc_screen);
// trying to transform the current image container location into percentage
// so when the image container is exaclty in the middle of the screen percentage should be zero
// and as the image container getting closer to the edges of the screen should increase to 100%
int final_loc = ((loc_screen[1]-center)*100)/half_screen_height;
// translate the inner image taking consideration also the density of the screen
mImageView.setTranslationY(-final_loc * 0.4f * density);
}
}
}
I hope it can help someone that is looking for similar functionality.

scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
int top = scrollView.getScrollY(); // Increases when scrolling up ^
if(top != 0) {
int newTop = (int) (top * .5f);
imageFrame.setTop(newTop < 0 ? 0 : newTop);
}
}
});

Related

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/

Scaling a right aligned layout doesn't work as expected

I have a RelativeLayout with a ImageView inside it, it is aligned to the right of the screen with
android:layout_alignParentRight="true"
I then apply an animation to the layout, that will scale it to twice it size (on X-axis), but for some reason the alignment is broken and the layout (and image within it) stretches outside of the screen to the right. I was expecting it to grow to the left since it is aligned to the parent right.
I guess I could apply a translate to -X at the same time, but there are problems with this as well (1. it´s a bit complicated to compute, 2. the fillAfter never seems to work when using an AnimationSet).
Does anyone know how to solve this problem smoothly? :)
Apparently, Androids scaling is doing all sorts of bad stuff, for example it does not scale a 9-patch correct. Here is a custom animation for scaling that will solve the above and is compatible with 9-patch images. I also got some info here for the basics, this is just a rewrite of his solution: How to implement expandable panels in Android?
public class ExpandAnimation extends Animation
{
private final int mStartWidth;
private final int mDeltaWidth;
private View view;
public ExpandAnimation(View view, int startWidth, int endWidth)
{
mStartWidth = startWidth;
mDeltaWidth = endWidth - startWidth;
this.view = view;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
android.view.ViewGroup.LayoutParams lp = view.getLayoutParams();
lp.width = (int) (mStartWidth + mDeltaWidth * interpolatedTime);
view.setLayoutParams(lp);
view.invalidate();
}
#Override
public boolean willChangeBounds()
{
return true;
}
}

Align the child views in center of the ViewPager android

I need to set the child view as center of the ViewPager and also I would like to show some part of the next and previous views to the current view sides(like current screen below 1). But currently the current view is starting at left side of the ViewPager(like expected screen below 2). How can I achieve that?
Here is my code..
MyViewPagerAdapter
public class MyViewPagerAdapter extends PagerAdapter {
private Activity mActivity;
private int mPageCount;
public MyViewPagerAdapter(Activity activity,int pageCount) {
mActivity = activity;
mPageCount = pageCount;
}
#Override
public int getCount() {
return mPageCount;
}
#Override
public boolean isViewFromObject(View view, Object obj) {
return (view ==(View)obj);
}
#Override
public Object instantiateItem(ViewGroup container,final int position) {
ViewGroup viewGroup = (ViewGroup)mActivity.getLayoutInflater().inflate(
R.layout.item_view, null);
viewGroup.setBackgroundColor(randomColor());
TextView textView = (TextView)viewGroup.findViewById(R.id.textView1);
textView.setText("Page: "+(position+1));
Button button = (Button) viewGroup.findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mActivity, "Hey, Its clicked!!! at page "+(position+1), Toast.LENGTH_LONG).show();
}
});
container.addView(viewGroup);
return viewGroup;
}
Random rnd = new Random();
private int randomColor(){
return Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
}
#Override
public void destroyItem(ViewGroup collection, int position, Object view) {
//must be overridden else throws exception as not overridden.
Log.d("Tag", collection.getChildCount()+"");
collection.removeView((View) view);
}
#Override
public float getPageWidth(int position) {
return 0.8f;
}
}
MainActivity
public class MainActivity extends Activity {
private ViewPager viewPager;
LinearLayout linearLayout;
private int ID = 100;
private final int count = 8;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = (ViewPager) findViewById(R.id.viewPager);
linearLayout = (LinearLayout) findViewById(R.id.indicator_layout);
generateIndicators(count);
MyViewPagerAdapter adapter = new MyViewPagerAdapter(this, count);
viewPager.setAdapter(adapter);
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
int oldPosition = 0;
#Override
public void onPageSelected(int position) {
//this changes the old position's view state image
((TextView)linearLayout.getChildAt(oldPosition)).setText("");
oldPosition = position;
//this changes the current position's view state image
((TextView)linearLayout.getChildAt(position)).setText((position+1)+"");
}
//this method will be called repeatedly upto another item comes as front one(active one)
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
//this will be called as per scroll state
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
viewPager.setOffscreenPageLimit(4);
}
private void generateIndicators(int count) {
/// Converts 14 dip into its equivalent px
int padd = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics());
for(int i=0;i<count;i++){
TextView textView = new TextView(this);
textView.setId(ID+i);
final int currentItem = i;
textView.setBackgroundResource(R.drawable.white_cell);
textView.setPadding(padd,padd,padd,padd);
/// Converts 14 dip into its equivalent px
int size = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics());
textView.setTextSize(size);
textView.setGravity(Gravity.CENTER);
/// Converts 14 dip into its equivalent px
int px = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, getResources().getDisplayMetrics());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(px, px);
linearLayout.addView(textView,params);
}
((TextView)linearLayout.getChildAt(0)).setText("1");
}
}
activity_main.xml
<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"
tools:context=".MainActivity" >
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
</android.support.v4.view.ViewPager>
<LinearLayout
android:id="#+id/indicator_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="19dp" >
</LinearLayout>
</RelativeLayout>
item_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="match_parent"
android:id="#+id/root_view"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="click me" />
</LinearLayout>
Current screen
expected screen
For one app I implemented similar the following way, with standard ViewPager:
Make pages full-screen with the actual content in an inner layout. For example, make the full-screen layout a RelativeLayout with transparent background and the actual content another RelativeLayout centered in the parent. If I remember right, the reason for this was that with just the inner layout as a page, the ViewPager would not have taken all the screen width on some devices such as Galaxy Nexus.
Use ViewPager.setPageMargin() to set up a negative page margin i.e. how much of the next/previous page you want to show. Make sure it only overlaps the transparent region of the parent full-screen layout.
Call ViewPager.setOffscreenPageLimit() to adjust the off-screen page count to at least 2 from the default 1 to ensure smooth paging by really creating the pages off-screen. Otherwise you will see next/previous pages being drawn while already partially showing on screen.
For anyone upset that the OP didn't update his question with the solution here is a link that explains, with minimal effort, how to pull this off in XML: http://blog.neteril.org/blog/2013/10/14/android-tip-viewpager-with-protruding-children/
Basically when you declare your viewpager in XML, give it the same left and right padding and set android:clipToPadding="false". (The clipToPadding is missing in his xml sample and necessary to achieve this effect)
Finally, I have added my solution for this question in GitHub. I have done some pretty tricks to get the workaround solution. You can get the project from the below link(Actually I have planned to create a blog with the explanation , but I dint have that much time to do).
Here is the link(https://github.com/noundla/Sunny_Projects/tree/master/CenterLockViewPager)
You have to copy the files from com.noundla.centerviewpagersample.comps package to your project. And you can see the usage of that Viewpager in MainActivity class.
Please let me know if anyone has problems with this.
I found solution in this post, below the code i used:
// Offset between sibling pages in dp
int pageOffset = 20;
// Visible part of sibling pages at the edges in dp
int sidePageVisibleWidth = 10;
// Horizontal padding will be
int horPadding = pageOffset + sidePageVisibleWidth;
// Apply parameters
viewPager.setClipToPadding(false);
viewPager.setPageMargin(UIUtil.dpToPx(pageOffset, getContext()));
viewPager.setPadding(UIUtil.dpToPx(horPadding, getContext()), 0, UIUtil.dpToPx(horPadding, getContext()), 0);
dpToPx code:
public static int dpToPx(int dp, Context context) {
float density = context.getResources().getDisplayMetrics().density;
return Math.round((float) dp * density);
}
This is all you need
You can use padding for viewPager and set clipToPadding false
Java
viewPager.setClipToPadding(false);
viewPager.setPadding(50, 0, 50, 0);
Kotlin
viewPager.clipToPadding = false
viewPager.setPadding(50, 0, 50, 0)
I had to center current page in view pager with different page widths, so solution with paddings was not suitable. Also user scrolling was disabled (it was tab bar view pager, scrolled by another view pager). Here is a very simple solution to do that - just override ViewPager.ScrollTo method just like this (C# code, Xamarin):
public override void ScrollTo(int x, int y)
{
x -= (int) (MeasuredWidth * (1 - Adapter.GetPageWidth(CurrentItem)) / 2);
base.ScrollTo(x, y);
}
And if you calculate page width for each fragment don't forget to cache them in array.
Extend HorizontalScrollView class as the parent for the scrolling view. In the onMeasure() method you can specify the width and height of each child. Little cumbersome way but the effect will be good and you can have a good hold on your child view.

create a 16:9 view

I am writing a program that I would like to have a 16:9 screen all the time. I set up a basic linearlayout to host 2 object that I write. The first one overwrite the onMeasure method, so that it'll take a "square" space from the screen, and the second object take the rest. This looks good on a 16:9 device that I have. But when I tried it out on other device, it just looks bad. I tried to extend from the linearlayout that host my object, and overwrite the onMeasure method for the layout. The custom Linearlayout seems to do the 16:9 fine, but my first object (the square), is still getting the big square, not a smaller square confined to the 16:9 strip. Here are the relavent code
MainActivity:
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
layout = new MyLinear(this);
mField = new Field(this);
mField.setId(1);
control = new Controller(this);
control.setId(2);
layout.addView(mField);
layout.addView(control);
registerForContextMenu(control);
setContentView(layout);
mField.requestFocus();
}
the onMeasure code for the custom linearlayout looks like this
public class MyLinear extends LinearLayout{
private int height, width; // dimension of the screen
private Context m_context;
public MyLinear(Context context) {
super(context);
m_context=context;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height_temp = View.MeasureSpec.getSize(heightMeasureSpec);
int width_temp = View.MeasureSpec.getSize(widthMeasureSpec);
double ratio = width_temp/(double)height_temp;
int final_height = (int) (width_temp/1.77);
height=final_height;
if (ratio<1.7) {
setMeasuredDimension(width_temp, (int) (width_temp/1.77));
} else {
setMeasuredDimension(width_temp, height_temp);
}
}
anyone has a better suggestion?
You edit, view.xml file. Add res/layout-xlarge/my_layout.xml folder. after add my_layout.xml file res/layout-xlarge/ folder... I'm sorry for bad english

Getting the width/height of a layout in Android

I'm wondering how to measure the dimensions of a view. In my case it is aan Absolute Layout. I've read the answers concerning those questions but I still don't get it.
This is my code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
AbsoluteLayout layoutbase = (AbsoluteLayout) findViewById(R.id.layoutbase);
drawOval();
}
public void drawOval(){ //, int screenWidth, int screenHeight){
AbsoluteLayout layoutbase = (AbsoluteLayout) findViewById(R.id.layoutbase);
int screenWidth = layoutbase.getWidth();
int screenHeight = layoutbase.getHeight();
Log.i("MyActivity", "screenWidth: " + screenWidth + ", screenHeight: " +screenHeight);
Coordinates c = new Coordinates(BUTTONSIZE,screenWidth,screenHeight);
...some code ...
((ViewGroup) layoutbase ).addView(mybutton, new AbsoluteLayout.LayoutParams(BUTTONSIZE, BUTTONSIZE, c.mX, c.mY));
mybutton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
showText(mybutton);
}
});
}
public void showText(View button){
int x = findViewById(LAYOUT).getWidth();
int y = findViewById(LAYOUT).getHeight();
Toast message = Toast.makeText(this, "x: " + x , Toast.LENGTH_SHORT);
message.show();
}
The getWidth() command works great in showText() but it does not in drawOval(). I know it looks a bit different there but I also used the int x = findViewById(LAYOUT).getWidth(); version in drawOval(), and x/y are always 0. I don't really understand why there seems to be no width/height at that earlier point. Even if I actually draw a Button on the Absolute Layout, getWidth() returns 0. Oviously I want to measure the sizes in drawOval().
I think will help you.
LinearLayout headerLayout = (LinearLayout)findviewbyid(R.id.headerLayout);
ViewTreeObserver observer = headerLayout .getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
int headerLayoutHeight= headerLayout.getHeight();
int headerLayoutWidth = headerLayout.getWidth();
headerLayout .getViewTreeObserver().removeGlobalOnLayoutListener(
this);
}
});
}
getWidth() is giving you 0 because onCreate is called before layout actually happens. Due to views being able to have dynamic positions and sizes based on attributes or other elements (fill_parent for example) there's not a fixed size for any given view or layout. At runtime there is a point in time (actually it can happen repeatedly depending on many factors) where everything is actually measured and laid out. If you really need the height and width, you'll have to get them later as you've discovered.
This specially deal with Dimensions so
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
This may help you in managing dimensions.
Note: This returns the display dimensions in pixels - as expected. But the getWidth() and getHeight() methods are deprecated. Instead you can use:
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
as also Martin Koubek suggested.
If your goal is to simply draw an oval on the screen, then consider creating your own custom View rather than messing around with AbsoluteLayout. Your custom View must override onDraw(android.graphics.Canvas), which will be called when the view should render its content.
Here is some extremely simple sample code that might help get you started:
public class MainActivity extends Activity {
private final Paint mPaint = new Paint();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleView(this));
}
// create a nested custom view class that can draw an oval. if the
// "SampleView" is not specific to the Activity, put the class in
// a new file called "SampleView.java" and make the class public
// and non-static so that other Activities can use it.
private static class SampleView extends View {
public SampleView(Context context) {
super(context);
setFocusable(true);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.CYAN);
// smoothen edges
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(4.5f);
// set alpha value (opacity)
mPaint.setAlpha(0x80);
// draw oval on canvas
canvas.drawOval(new RectF(50, 50, 20, 40), mPaint);
}
}
}
This give you screen resolution:
WindowManager wm = (WindowManager)context.getSystemService(context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point outSize = new Point();
display.getSize(outSize);
kabuko's answer is correct, but could be a little more clear, so let me clarify.
getWidth() and getHeight() are (correctly) giving you 0 because they have not been drawn in the layout when you call them. try calling the two methods on the button after addView() (after the view has been drawn and is present in the layout) and see if that gives you the expected result.
See this post for more information.

Categories

Resources