Android -- How to Position View Off-Screen

Android -- How to position View off-screen?

I figured out a solution to this that should be easy to implement. It involves modifying the layout and the Activity inflating the layout... see below:

Activity (QuickPlay.java):

public class QuickPlay extends Activity implements AnimationListener
{
private ImageView myImageView;
private LinearLayout LL;

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.quick_play_screen);

myImageView = (ImageView) this.findViewById(R.id.Clip);
LL = (LinearLayout) this.findViewById(R.id.QuickPlayClipLayout);

//finally
Animation anim = AnimationUtils.loadAnimation(this, R.anim.slide_in_quickplay);
anim.setAnimationListener(this);
LL.startAnimation(anim);
}
@Override
public void onAnimationEnd(Animation animation){}

@Override
public void onAnimationRepeat(Animation animation){}

@Override
public void onAnimationStart(Animation animation)
{
// This is the key...
//set the coordinates for the bounds (left, top, right, bottom) based on the offset value (50px) in a resource XML
LL.layout(0, -(int)this.getResources().getDimension(R.dimen.quickplay_offset),
LL.getWidth(), LL.getHeight() + (int)this.getResources().getDimension(R.dimen.quickplay_offset));
}
}

New LinearLayout (CustomLinearLayout.java):

public class CustomLinearLayout extends LinearLayout
{
private Context myContext;

public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
myContext = context;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec+((int)myContext.getResources().getDimension(R.dimen.quickplay_offset)));
}
}

Layout (/res/layout/quick_play_screen.xml):

<?xml version="1.0" encoding="utf-8"?>
<com.games.mygame.CustomLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:id="@+id/QuickPlayClipLayout">
<ImageView android:id="@+id/Clip"
android:background="@drawable/clip"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</ImageView>
</com.games.mygame.CustomLinearLayout>

Resource (/res/values/constants.xml):

<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="quickplay_offset">50dp</dimen>
</resources>

Animation (/res/anim/slide_in_quickplay.xml):

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="100%p"
android:toYDelta="0"
android:duration="1000"/>
<alpha android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="1000" />
</set>

The program now does exactly what I need it to do. The entire layout starts off screen at the bottom, slides in in 1 sec and comes to a rest where the top of the layout is actually 50px off the top of the screen (i.e. LL.getTop() = -50) and the bottom of the layout is resting at the bottom of the screen (i.e. LL.getBottom() = 530 = 480 + 50).

How to position a view in Android constraint layout outside the screen at the beginning

Okay so to have a negative margin you can use translateX, translateY or TranslationZ.

in xml like so:

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello World!"
android:translationX="-60dp"
android:translationY="-90dp"
android:translationZ="-420dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

or programmatically like so:

View view = ...;
view.setTranslationX(-60);
view.setTranslationY(-90);
view.setTranslationZ(-420);

Then in order to slowly bring it in from right to left you can use the animate() method like so:

View view = ...;
view.animate().setDuration(1000).translationX(-600).start();

How to position a view off-screen so that it can be Animated to move on-screen?

In the end, I used a trick: I combined AnimationDrawable and view animation. I did the following (assuming that the animation has to run T milliseconds):

  1. Position that ImageView on-screen.
  2. Set as its background an AnimationDrawable with following frames:
    • empty Drawable: 1ms,
    • normal Drawable: Tms.
  3. Change view's animation to this:
    • jump to off-screen position (translation with duration=1ms),
    • do normal animation.

how to move a view from out of screen to bottom of screen with animation?

I have a solution, that is using 2 views, startView and destinationView, this way to move startView to destinationView

Assume we have 2 views: startView and destinationView

int[] startLoc = new int[2];
int[] desLoc = new int[2];
startView.getLocationOnScreen(startLoc);
destinationView.getLocationOnScreen(desLoc);
float x = desLoc[0]
- startLoc[0]
- (startView.getWidth() - destinationView.getWidth()) / 2;
float y = desLoc[1]
- startLoc[1]
- (startView.getHeight() - destinationView.getHeight()) / 2;
createMoveAndScaleThenFadeAnimation(
startView, x, y, 1.0f, 1, 1000, 300,
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
}).start();

and createMoveAndScaleThenFadeAnimation() method:

public AnimatorSet createMoveAndScaleThenFadeAnimation(View view, float deltaX,
float deltaY, float scaleFrom, float scaleTo, int duration, int offset,
@Nullable final AnimatorListenerAdapter listener) {
ObjectAnimator animX = ObjectAnimator.ofFloat(view, "translationX", deltaX);
ObjectAnimator animY = ObjectAnimator.ofFloat(view, "translationY", deltaY);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", scaleFrom, scaleTo);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", scaleFrom, scaleTo);
ObjectAnimator fade = ObjectAnimator.ofFloat(view, "alpha", 1.0f, 0.0f);
AnimatorSet animSet = new AnimatorSet();
animSet.setDuration(duration);
animSet.setStartDelay(offset);
animSet.play(animX).with(animY).with(scaleX).with(scaleY).before(fade);
if (listener != null) {
animSet.addListener(listener);
}
return animSet;
}

so now we don't care about device's screen size or any thing else, just define 2 view in layout and do this way. In your case, we can set view 2th visibility is INVISIBLE.

NOTE

Please make sure whole view has been inflated before do this way, if not, we can not get view's position.

Option 1: Using OnGlobalLayoutListener()

ViewTreeObserver vto=view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener(){
@Override public void onGlobalLayout(){
//Here we can get the view's position and view size
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}

option 2 override onWindowFocusChanged()

@Override
public void onWindowFocusChanged(boolean hasFocus) {
// TODO Auto-generated method stub
super.onWindowFocusChanged(hasFocus);
// Here we can get the view's position and view size
}

Thank @Azin Nilchi for the suggestion.

hope this helps!

Animate view off Screen in android Constraint Layout

Instead of clearing the bottom constraint of the TextView, try constraining its bottom to the top of the ConstraintLayout like this:

constraintSet.connect (R.id.txt_PackageTitle, 
ConstraintSet.BOTTOM,
PARENT_ID,
ConstraintSet.TOP);

Now when the view is animated, it should slide off the top edge.

Negative margins are not supported with ConstraintLayout as is noted here.

How to keep imageView going offscreen [Kotlin]

Welcome to Kotlin!

So yeah, you've got your Spongebob animating, now you need some logic to control that animation. The problem here is you don't always want that full animation to happen, right? If he's too close to the edge of the screen, you want the button to only move him as far as that invisible wall (and if he's right against it, that means no movement at all).

The animation and drawing systems don't put any restrictions on where you can put a View, so it's up to you to handle that yourself. You basically need to do this when a button is clicked:

  • get the Spongebob's position coordinates (it's the X you really care about right now)
  • work out the position of the edge you care about (the View's coordinates describe where the top left corner is, so if you're looking at the right edge, you need that X coordinate + the width of the View)
  • work out the X coordinate of the edge of the screen (or parent layout, whatever you want the Spongebob to be contained within)
  • if the distance between the Spongebob edge and the screen edge is less than your normal animation movement, you need to change it to that remaining distance
  • you'll also want to work out the appropriate duration too, if he's moving half the usual distance the animation should take half as long

that's a lot to work on, and there are a few ways to do it, but here's one approach just using the screen edges as the bounds

    import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import kotlin.math.abs
import kotlin.math.min

private const val MOVE_DISTANCE = 100
private const val MOVE_TIME = 90

class MainActivity : AppCompatActivity() {
private var screenWidth = 0
private lateinit var spongeBob : View

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.spongebob)

// store this when the Activity is created, if the device is rotated the Activity
// will be recreated and this method gets run again
screenWidth = applicationContext.resources.displayMetrics.widthPixels

//val picture = findViewById<ImageView>(R.id.SpongeBob)
val right_button = findViewById<Button>(R.id.right)
val left_button = findViewById<Button>(R.id.left)
spongeBob = findViewById(R.id.spongeBob)

right_button.setOnClickListener()
{
// two possible values - the distance to the edge, and the normal amount we move
// we want the smaller of the two (i.e. always move the normal amount, unless
// the edge is closer than that
val distance = min(distanceToEdge(left = false), MOVE_DISTANCE)
moveSpongeBob(distance)
}

left_button.setOnClickListener()
{
val distance = min(distanceToEdge(left = true), MOVE_DISTANCE)
// we're moving left so we need to use a negative distance
moveSpongeBob (-distance)
}
}

private fun distanceToEdge(left: Boolean): Int {
// Get the Spongebob's top-left position - the call is a method on the View class,
// I'm assuming SpongeBob is a View, and you need to pass an array in because
// that's just how it works for whatever reason...
val location = IntArray(2)
spongeBob.getLocationOnScreen(location)
val x = location[0]

// I'm just using the View.getWidth() call here (Kotlin style) but I don't know
// what the SpongeBob class is, so you'll need to handle this
// You could set this once, like when we get the screen width, but width will be 0 until
// the View is laid out - so you can't do it in #onCreate, #onViewCreated should work
val spongeBobWidth = spongeBob.width

// the left edge is just the x position, however far that is from zero
return if (left) x
// the right edge is the x position plus the width of the bob
else screenWidth - (x + spongeBobWidth)
}

// Actually move the view, by the given distance (negative values to move left)
private fun moveSpongeBob(distance: Int) {
// work out how much this distance relates to our standard move amount, so we can
// adjust the time by the same proportion - converting to float so we don't get
// integer division (where it's rounded to a whole number)
val fraction = distance.toFloat() / MOVE_DISTANCE
// distance can be negative (i.e. moving left) so we need to use the abs function
// to make the duration a postitive number
val duration = abs(MOVE_TIME * fraction).toLong()
spongeBob.animate().setDuration(duration).translationXBy(distance.toFloat())
}
}

There's nicer stuff you can do (and SpongeBob should be called spongeBob and be a View) but that's the basics. This article on the coordinate system might help you out too.



Related Topics



Leave a reply



Submit