Animated transition between two imageview - java

On one hand I have a layout with two ImageView :
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/image_cross2"
android:layout_width="wrap_content"
android:layout_height="#dimen/image_size"
android:layout_gravity="top"
android:scaleType="centerCrop" />
<ImageView
android:id="#+id/image_cross1"
android:layout_width="wrap_content"
android:layout_height="#dimen/image_size"
android:layout_gravity="top"
android:scaleType="centerCrop" />
</FrameLayout>
On the other hand i have a list of image ressources :
static int mImages[] = new int[] {
R.drawable.artistes,
R.drawable.couple1,
R.drawable.couple2,
R.drawable.couple3,
R.drawable.enfant,
R.drawable.manege,
R.drawable.manege2,
R.drawable.metropolitain,
R.drawable.panoramique,
R.drawable.sacrecoeur };
i have also a Scheduler made from Handler + postDelayed() to display the images one after another with a timer. This is working fine
my issue is about the transition animation from one imageview to the other, knowing that i have to clean the imageviews each time to avoid OutOfMemoryExceptions :
For now i do that in the schduled callback method :
if (mIndex == mImages.length) {
mIndex = 0; // repeat
}
if (mIndex % 2 != 0) { // pair
mImageCross2.setImageResource(mImages[mIndex++]);
Utils.crossfade(mImageCross2, mImageCross1, 1000/*duration*/);
mImageCross1.setImageResource(0);
} else {
mImageCross1.setImageResource(mImages[mIndex++]);
Utils.crossfade(mImageCross1, mImageCross2, 1000);
mImageCross2.setImageResource(0);
}
with this animation :
public static void crossfade(final ImageView viewIn, final ImageView viewOut, int duration) {
Animation fadeIn = new AlphaAnimation(0, 1);
fadeIn.setDuration(duration);
Animation fadeOut = new AlphaAnimation(1, 0);
fadeOut.setDuration(duration);
fadeOut.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
viewOut.setVisibility(View.GONE);
}
});
fadeIn.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
viewIn.setVisibility(View.VISIBLE);
}
});
viewOut.startAnimation(fadeOut);
viewIn.startAnimation(fadeIn);
}
The animation is not great, the fade is not really smooth, what can i do to make it more smooth while having to clean the ImageView each times ?

My first suggestion is to simplify most of your code. Android has this nice little class called TransitionDrawable
so you can have just 1 image view and use the TransitionDrawable:
TransitionDrawable td = new TransitionDrawable( new Drawable[] {
getResources().getDrawables(mImages[x]),
getResources().getDrawables(mImages[y])
});
imageView.setImageDrawable(td);
and call the animation on td with
td.startTransition(1000);
// and
td.reverseTransition(1000);
and keep using the postDelayed to trigger them

Related

Animation doesn't end - Android

In my app, I have a button to show a drop-down menu, inside of that menu we have some options, one of this is "flip A coin",
the purpose of this option is to flip a coin easy animation, that animation appears inside a textView, and show a head-side or a tail-side of a coin instead of the text in the textView.
I have two problems:
The animation doesn't work how I want, it should appear in 1 second a coin instead of the text inside the textView, it stays there and after 2 seconds he disappears, but after the disappearance, the coin image come back inside the textView, it shouldn't reappear.
This is not a real question for a problem but more an optional question like "you know how to do that?". I 'don't know how to create a flip animation with a coin multiple rotations.
XML drop down menu:
<item
android:id="#+id/flipacoin"
android:title="#string/flipACoin" />
<item
android:id="#+id/rolladice"
android:title="#string/rollADice" />
<item
android:id="#+id/imagebackground"
android:title="#string/changeImage" />
JAVA code that calls the animation function:
#Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()){
case R.id.flipacoin:
flipACoin();
return true;
case R.id.rolladice:
Toast.makeText(this,"TODO roll a dice",Toast.LENGTH_SHORT).show();
return true;
case R.id.imagebackground:
Toast.makeText(this,"TODO image background",Toast.LENGTH_SHORT).show();
return true;
default:
return false;
}
}
JAVA animation function:
public void flipACoin(){
coin.setText(null); //this is for remove the text inside the textView
coin.setBackground(RANDOM.nextFloat() > 0.5f ? getResources().getDrawable(R.drawable.tails) : getResources().getDrawable(R.drawable.heads));
Animation fadeIn = new AlphaAnimation(0, 1);
fadeIn.setInterpolator(new DecelerateInterpolator());
fadeIn.setDuration(1000);
Animation fadeOut = new AlphaAnimation(1, 0);
fadeOut.setInterpolator(new AccelerateInterpolator());
fadeOut.setStartOffset(2000);
fadeOut.setDuration(1000);
AnimationSet animation = new AnimationSet(false);
animation.addAnimation(fadeIn);
animation.addAnimation(fadeOut);
coin.setAnimation(animation);
}
When You set background at the beginning it just stays after animation. To set back text and background to null You can add Animation listener. Below is sample application which does it:
public class MainActivity extends AppCompatActivity
{
TextView coin;
Random RANDOM;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RANDOM = new Random();
coin = findViewById(R.id.coin);
(findViewById(R.id.click)).setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
flipACoin();
}
});
}
public void flipACoin()
{
coin.setText(null);
coin.setBackground(ResourcesCompat.getDrawable(getResources(),
RANDOM.nextFloat() > 0.5f ? R.drawable.ic_launcher_background : R.drawable.ic_launcher_foreground,
null
));
Animation fadeIn = new AlphaAnimation(0, 1);
fadeIn.setInterpolator(new DecelerateInterpolator());
fadeIn.setDuration(1000);
Animation fadeOut = new AlphaAnimation(1, 0);
fadeOut.setInterpolator(new AccelerateInterpolator());
fadeOut.setStartOffset(2000);
fadeOut.setDuration(1000);
AnimationSet animation = new AnimationSet(false);
animation.addAnimation(fadeIn);
animation.addAnimation(fadeOut);
// listener, it will execute function when animation starts/ends/repeats
animation.setAnimationListener(new Animation.AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
Log.d("MyTag", "onAnimationStart:");
}
#Override
public void onAnimationEnd(Animation animation) // when animation ends, set text and background to null
{
Log.d("MyTag", "onAnimationEnd:");
coin.setBackground(null);
coin.setText("Default");
}
#Override
public void onAnimationRepeat(Animation animation)
{
Log.d("MyTag", "onAnimationRepeat:");
}
});
coin.setAnimation(animation);
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<Button
android:id="#+id/click"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="click"
/>
<TextView
android:id="#+id/coin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Default"
/>
</androidx.appcompat.widget.LinearLayoutCompat>

Transition between decimal or double numbers

Imagine a number 10, then after user clicks a button it changes to 100. But how to make an efficient transition
10 -> 100,
that will display values like
12, 15, 18, ..., 97, 100 over 1 second.
I've seen something like that in "Cookie clicker" but couldn't find anything about that kind of transition in the source code.
I had an idea of a loop (for number1 < number2, do number1++), it will work fine for small numbers, but if 10 changes to 1 billion, then the loop will probably freeze the whole app.
Second idea is to get added value (100-10=90) and divide by 30 or 60 frames, and add this value with each frame. But what will happen if frame is dropped? - Probably value will not be added. What if user makes double click or the system adds values automatically?
Hope it gives an idea of what kind of number transition I need.
Maybe I overlooked and there is a simple approach? Any help is appreciated.
Hope this little demo using a ValueAnimator will inspire you to find an appropriate solution.
You can specify the duration of the animation (see code) and even adjust the frame-rate by saying mAnimator.setFrameDelay(frameDelay);.
By using animator.isRunning() or animator.isStarted() you can prevent double-click malfunction or other unwanted behaviour while the current animation is runnning.
The Main Activity:
/** ValueAnimator demo */
public class MainActivity extends Activity {
ValueAnimator mAnimator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView tv = (TextView) findViewById(R.id.textview);
mAnimator = ValueAnimator.ofInt(1, 100).setDuration(1000);
mAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mAnimator.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(final ValueAnimator animator) {
final Integer value = (Integer) animator.getAnimatedValue();
tv.setText(String.format("%04d", value));
}
});
mAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animator) {
super.onAnimationEnd(animator);
final int endValue = Integer.parseInt((String) tv.getText());
mAnimator.setIntValues(endValue, endValue + 100);
}
});
}
/** Button callback */
public void onClick(final View view) {
if (!mAnimator.isStarted() && !mAnimator.isRunning()) {
mAnimator.start();
}
}
}
Simple demo layout:
<LinearLayout 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"
android:orientation="vertical" >
<TextView
android:id="#+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:typeface="monospace"
android:text="0001" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Gimme +100"
android:onClick="onClick">
</Button>
Here's another demo (hope this answers your 2. question), which implements different behaviour dependent on single click or double-click on the button. Just experiment with it, you now have the basic building blocks to construct own behavour ...
/** ValueAnimator demo */
public class MainActivity extends Activity {
ValueAnimator mAnimator;
TextView mTv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTv = (TextView) findViewById(R.id.textview);
mAnimator = ValueAnimator.ofInt(1, 100).setDuration(1000);
mAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mAnimator.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(final ValueAnimator animator) {
final Integer value = (Integer) animator.getAnimatedValue();
mTv.setText(String.format("%04d", value));
}
});
final Button button = (Button) findViewById(R.id.button);
final GestureDetector gestureDetector = new GestureDetector(this,
new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onDoubleTap(MotionEvent e) {
performAnimation(100);
return true;
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
performAnimation(0);
return true;
}
});
button.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
}
/** starts animation */
private void performAnimation(final int offset) {
if (!mAnimator.isStarted() && !mAnimator.isRunning()) {
final int endValue = Integer.parseInt((String) mTv.getText());
mAnimator.setIntValues(endValue + offset, endValue + 100 + offset);
mAnimator.start();
}
}
}
Don't forget to replace your layout file, since the click-attribute of the button has been removed:
<LinearLayout 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"
android:orientation="vertical" >
<TextView
android:id="#+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:typeface="monospace"
android:text="0001" />
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Gimme +100" >
</Button>
</LinearLayout>
I guess you can do it by using different threads. Only main thread works with UI so you can divide the interval into small intervals and make a transitions in different threads.After send them to main thread and print. Hope it will help.

Android item swiping on listview

I'm struggling with this topic on Android Listview since a few days and I don't seem to get it right. I just can't understand how to do it. I've learned the best I could about Adapters (BaseAdapter specially) but still couldn't think in a way to do this.
I've searched on the web for information but didn't quite get it.
What I wish to do is the following: I want to create a ListView of
contacts.
Each row has 3 horizontal sections: a photo that is constant, content x and content y (this last one is out of the screen and is invisible)
And I wish that when the user swipes a single item
from right to left, the content (with information x) fades out. The
other content (with information Y) slides in from out of the screen,
with right to left orientation, at the same time.
When the user
swipes back (from left to right) the content y swipes out again and
the initial content x fades in.
I just can't to this, so I'm asking your help please.
Thank you very much for your time and effort
Adding such animations to a ListView is no problem, I built this solution for a normal non-custom ListView in a few minutes, generally this sort of thing works on any ListView out of the box, it's all just in the adapter. The only thing missing in my answer is the swipe detection, unfortunately I don't have the time to test that right now. But swipe detection is not difficult and there are a ton of examples if you google it. Anyway if you have questions, feel free to ask.
Result:
I am using a simple BaseAdapter with ViewHolder pattern, nothing special, I will post the getView method anyway for clarification:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(getItemId(position) == TEST_VIEW_ID) {
TestViewModel viewModel = (TestViewModel) getItem(position);
TestRow row;
if(convertView == null) {
convertView = this.inflater.inflate(TestRow.LAYOUT, parent, false);
row = new TestRow(convertView); // Here the magic happens
convertView.setTag(row);
}
row = (TestRow) convertView.getTag();
row.bind(viewModel);
}
return convertView;
}
In my ViewHolder class, here called TestRow I created a few helper methods for the animations, I will explain them further below, but here first my code from TestRow:
public class TestRow {
public static final int LAYOUT = R.layout.list_item_test;
public ImageView ivLogo;
public TextView tvFadeOut;
public TextView tvSlideIn;
public TestRow(View view) {
this.ivLogo = (ImageView) view.findViewById(R.id.ivLogo);
this.tvFadeOut = (TextView) view.findViewById(R.id.tvFadeOut);
this.tvSlideIn = (TextView) view.findViewById(R.id.tvSlideIn);
this.ivLogo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// When the ImageView is clicked the animations are applied to the TextViews.
if(tvFadeOut.getVisibility() == View.VISIBLE) {
fadeOutView(tvFadeOut);
slideInView(tvSlideIn);
} else {
fadeInView(tvFadeOut);
slideOutView(tvSlideIn);
}
}
});
}
public void bind(TestViewModel viewModel) {
// Nothing to do here
}
}
And here are the helper methods i use for the animations:
private void fadeOutView(View view) {
Animation fadeOut = AnimationUtils.loadAnimation(view.getContext(), R.anim.fade_out);
if (fadeOut != null) {
fadeOut.setAnimationListener(new ViewAnimationListener(view) {
#Override
protected void onAnimationStart(View view, Animation animation) {
}
#Override
protected void onAnimationEnd(View view, Animation animation) {
view.setVisibility(View.GONE);
}
});
view.startAnimation(fadeOut);
}
}
private void fadeInView(View view) {
Animation fadeIn = AnimationUtils.loadAnimation(view.getContext(), R.anim.fade_in);
if (fadeIn != null) {
fadeIn.setAnimationListener(new ViewAnimationListener(view) {
#Override
protected void onAnimationStart(View view, Animation animation) {
view.setVisibility(View.VISIBLE);
}
#Override
protected void onAnimationEnd(View view, Animation animation) {
}
});
view.startAnimation(fadeIn);
}
}
private void slideInView(View view) {
Animation slideIn = AnimationUtils.loadAnimation(view.getContext(), R.anim.slide_in_right);
if (slideIn != null) {
slideIn.setAnimationListener(new ViewAnimationListener(view) {
#Override
protected void onAnimationStart(View view, Animation animation) {
view.setVisibility(View.VISIBLE);
}
#Override
protected void onAnimationEnd(View view, Animation animation) {
}
});
view.startAnimation(slideIn);
}
}
private void slideOutView(View view) {
Animation slideOut = AnimationUtils.loadAnimation(view.getContext(), R.anim.slide_out_right);
if (slideOut != null) {
slideOut.setAnimationListener(new ViewAnimationListener(view) {
#Override
protected void onAnimationStart(View view, Animation animation) {
}
#Override
protected void onAnimationEnd(View view, Animation animation) {
view.setVisibility(View.GONE);
}
});
view.startAnimation(slideOut);
}
}
private abstract class ViewAnimationListener implements Animation.AnimationListener {
private final View view;
protected ViewAnimationListener(View view) {
this.view = view;
}
#Override
public void onAnimationStart(Animation animation) {
onAnimationStart(this.view, animation);
}
#Override
public void onAnimationEnd(Animation animation) {
onAnimationEnd(this.view, animation);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
protected abstract void onAnimationStart(View view, Animation animation);
protected abstract void onAnimationEnd(View view, Animation animation);
}
These are the animation xml's I use:
fade in:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<alpha
android:fromAlpha="0"
android:toAlpha="1"
android:duration="700"/>
</set>
fade out:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<alpha
android:fromAlpha="1"
android:toAlpha="0"
android:duration="700"/>
</set>
slide in:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="100%" android:toXDelta="0%"
android:duration="700"/>
</set>
slide out:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="100%"
android:duration="700"/>
</set>
Use this xml in res/anim/ (FOR ANIMATION PURPOSE)
This is for left to right animation:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate android:fromXDelta="-100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="700"/>
</set>
This is for right to left animation:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="100%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="700" />
</set>
In your coding use intent like for left to right:
this.overridePendingTransition(R.anim.animation_enter,
R.anim.animation_leave);
In your coding use intent like for right to left
this.overridePendingTransition(R.anim.animation_leave,
R.anim.animation_enter);
For Custom List View u can use this Code:
http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/
Just catch Gesture event and apply animation, and make one layout which will appear-disappear on gesture event.
If u are Using for normal purpose than working example of exactly what u want is here:
https://github.com/47deg/android-swipelistview

Wrong updating TextView in Android app

I create simple Android app (https://www.linux.com/learn/docs/683628-android-programming-for-beginners-part-1) with latest Android Studio. Code:
public class test_act extends Activity {
private static final int MILLIS_PER_SECOND = 1000;
private static final int SECONDS_TO_COUNTDOWN = 30;
private android.widget.TextView countdownDisplay;
private android.os.CountDownTimer timer;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.full_act);
countdownDisplay = (android.widget.TextView) findViewById(R.id.time_display_box);
android.widget.Button startButton = (android.widget.Button) findViewById(R.id.startbutton);
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
try {
showTimer(SECONDS_TO_COUNTDOWN * MILLIS_PER_SECOND);
} catch (NumberFormatException e) {
// method ignores invalid (non-integer) input and waits
// for something it can use
}
}
});
}
private void showTimer(int countdownMillis) {
if(timer != null) { timer.cancel(); }
timer = new android.os.CountDownTimer(countdownMillis, MILLIS_PER_SECOND) {
#Override
public void onTick(long millisUntilFinished) {
countdownDisplay.setText("counting down: " +
millisUntilFinished / MILLIS_PER_SECOND);
}
#Override
public void onFinish() {
countdownDisplay.setText("KABOOM!");
}
}.start();
}
}
My 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" >
<TextView
android:id="#+id/time_display_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="60dp"
android:text="#string/_00_30"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<Button
android:id="#+id/startbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/time_display_box"
android:layout_centerHorizontal="true"
android:layout_marginTop="41dp"
android:text="#string/start" />
</RelativeLayout>
In emulator it's good working. But on my Galaxy S2 with CyanogenMod10.1(Android 4.2.2) app wrong updating TextView. Screenshot:
How I can resolve this problem?
upd: after screen rotate TextView is updating once.
You might want to try invalidating your layout every time it is updated. I am guessing with how often the text is being updated the phone is not having enough time to redraw the layout. This would also explain why it works when you rotate your phone, because then the layout is forced to update.
countdownDisplay.invalidate();
Let me know if that does not work.
It commonly happens when you put UI updates inside try blocks, try to avoid it or wrap with runOnUiThread.
EDIT:
Another reason - you update it to fast - you code does 1000 updates per second i dont think it can handle it.

Can I scroll a ScrollView programmatically in Android?

Is there any way to scroll a ScrollView programmatically to a certain position?
I have created dynamic TableLayout which is placed in a ScrollView. So I want that on a specific action (like clicking a Button, etc.) the particular row should scroll automatically to a top position.
Is it possible?
The answer from Pragna does not work always, try this:
mScrollView.post(new Runnable() {
public void run() {
mScrollView.scrollTo(0, mScrollView.getBottom());
}
});
or
mScrollView.post(new Runnable() {
public void run() {
mScrollView.fullScroll(mScrollView.FOCUS_DOWN);
}
});
if You want to scroll to start
mScrollView.post(new Runnable() {
public void run() {
mScrollView.fullScroll(mScrollView.FOCUS_UP);
}
});
ScrollView sv = (ScrollView)findViewById(R.id.scrl);
sv.scrollTo(0, sv.getBottom());
or
sv.scrollTo(5, 10);
I wanted the scrollView to scroll directly after onCreateView() (not after e.g. a button click). To get it to work I needed to use a ViewTreeObserver:
mScrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mScrollView.post(new Runnable() {
public void run() {
mScrollView.fullScroll(View.FOCUS_DOWN);
}
});
}
});
But beware that this will be called everytime something gets layouted (e.g if you set a view invisible or similar) so don't forget to remove this listener if you don't need it anymore with:
public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim) on SDK Lvl < 16
or
public void removeOnGlobalLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim) in SDK Lvl >= 16
There are a lot of good answers here, but I only want to add one thing. It sometimes happens that you want to scroll your ScrollView to a specific view of the layout, instead of a full scroll to the top or the bottom.
A simple example: in a registration form, if the user tap the "Signup" button when a edit text of the form is not filled, you want to scroll to that specific edit text to tell the user that he must fill that field.
In that case, you can do something like that:
scrollView.post(new Runnable() {
public void run() {
scrollView.scrollTo(0, editText.getBottom());
}
});
or, if you want a smooth scroll instead of an instant scroll:
scrollView.post(new Runnable() {
public void run() {
scrollView.smoothScrollTo(0, editText.getBottom());
}
});
Obviously you can use any type of view instead of Edit Text. Note that getBottom() returns the coordinates of the view based on its parent layout, so all the views used inside the ScrollView should have only a parent (for example a Linear Layout).
If you have multiple parents inside the child of the ScrollView, the only solution i've found is to call requestChildFocus on the parent view:
editText.getParent().requestChildFocus(editText, editText);
but in this case you cannot have a smooth scroll.
I hope this answer can help someone with the same problem.
Use something like this:
mScrollView.scrollBy(10, 10);
or
mScrollView.scrollTo(10, 10);
Try using scrollTo method More Info
If you want to scroll instantly then you can use :
ScrollView scroll= (ScrollView)findViewById(R.id.scroll);
scroll.scrollTo(0, scroll.getBottom());
OR
scroll.fullScroll(View.FOCUS_DOWN);
OR
scroll.post(new Runnable() {
#Override
public void run() {
scroll.fullScroll(View.FOCUS_DOWN);
}
});
Or if you want to scroll smoothly and slowly so you can use this:
private void sendScroll(){
final Handler handler = new Handler();
new Thread(new Runnable() {
#Override
public void run() {
try {Thread.sleep(100);} catch (InterruptedException e) {}
handler.post(new Runnable() {
#Override
public void run() {
scrollView.fullScroll(View.FOCUS_DOWN);
}
});
}
}).start();
}
**to scroll up to desired height. I have come up with some good solution **
scrollView.postDelayed(new Runnable() {
#Override
public void run() {
scrollView.scrollBy(0, childView.getHeight());
}
}, 100);
Yes, you can.
Let's say you got one Layout and inside that, you got many Views. So if you want to scroll to any View programmatically, you have to write the following code snippet:
For example:
content_main.xml
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="#+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/txtView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
MainActivity.java
ScrollView scrollView = (ScrollView) findViewById(R.id.scrollView);
Button btn = (Button) findViewById(R.id.ivEventBanner);
TextView txtView = (TextView) findViewById(R.id.ivEditBannerImage);
If you want to scroll to a specific View, let's say txtview, in this case, just write:
scrollView.smoothScrollTo(txtView.getScrollX(),txtView.getScrollY());
And you are done.
I got this to work to scroll to the bottom of a ScrollView (with a TextView inside):
(I put this on a method that updates the TextView)
final ScrollView myScrollView = (ScrollView) findViewById(R.id.myScroller);
myScrollView.post(new Runnable() {
public void run() {
myScrollView.fullScroll(View.FOCUS_DOWN);
}
});
Note: if you already in a thread, you have to make a new post thread, or it's not scroll new long height till the full end (for me).
For ex:
void LogMe(final String s){
runOnUiThread(new Runnable() {
public void run() {
connectionLog.setText(connectionLog.getText() + "\n" + s);
final ScrollView sv = (ScrollView)connectLayout.findViewById(R.id.scrollView);
sv.post(new Runnable() {
public void run() {
sv.fullScroll(sv.FOCUS_DOWN);
/*
sv.scrollTo(0,sv.getBottom());
sv.scrollBy(0,sv.getHeight());*/
}
});
}
});
}
Adding another answer that does not involve coordinates.
This will bring your desired view to focus (but not to the top position) :
yourView.getParent().requestChildFocus(yourView,yourView);
public void RequestChildFocus (View child, View focused)
child - The child of this ViewParent that wants focus. This view will contain the focused view. It is not necessarily the view that actually has focus.
focused - The view that is a descendant of child that actually has focus
Everyone is posting such complicated answers.
I found an easy answer, for scrolling to the bottom, nicely:
final ScrollView myScroller = (ScrollView) findViewById(R.id.myScrollerView);
// Scroll views can only have 1 child, so get the first child's bottom,
// which should be the full size of the whole content inside the ScrollView
myScroller.smoothScrollTo( 0, myScroller.getChildAt( 0 ).getBottom() );
And, if necessary, you can put the second line of code, above, into a runnable:
myScroller.post( new Runnable() {
#Override
public void run() {
myScroller.smoothScrollTo( 0, myScroller.getChildAt( 0 ).getBottom() );
}
}
It took me much research and playing around to find this simple solution. I hope it helps you, too! :)
just page scroll:
ScrollView sv = (ScrollView) findViewById(your_scroll_view);
sv.pageScroll(View.FOCUS_DOWN);
I was using the Runnable with sv.fullScroll(View.FOCUS_DOWN);
It works perfectly for the immediate problem, but that method makes ScrollView take the Focus from the entire screen, if you make that AutoScroll to happen every time, no EditText will be able to receive information from the user, my solution was use a different code under the runnable:
sv.scrollTo(0, sv.getBottom() + sv.getScrollY());
making the same without losing focus on important views
greetings.
it's working for me
mScrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mScrollView.post(new Runnable() {
public void run() {
mScrollView.fullScroll(View.FOCUS_DOWN);
}
});
}
});
private int totalHeight = 0;
ViewTreeObserver ScrollTr = loutMain.getViewTreeObserver();
ScrollTr.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
loutMain.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
loutMain.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
TotalHeight = loutMain.getMeasuredHeight();
}
});
scrollMain.smoothScrollTo(0, totalHeight);
I had to create Interface
public interface ScrollViewListener {
void onScrollChanged(ScrollViewExt scrollView,
int x, int y, int oldx, int oldy);
}
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;
public class CustomScrollView extends ScrollView {
private ScrollViewListener scrollViewListener = null;
public ScrollViewExt(Context context) {
super(context);
}
public CustomScrollView (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomScrollView (Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScrollViewListener(ScrollViewListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, l, t, oldl, oldt);
}
}
}
<"Your Package name ".CustomScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true"
android:scrollbars="vertical">
private CustomScrollView scrollView;
scrollView = (CustomScrollView)mView.findViewById(R.id.scrollView);
scrollView.setScrollViewListener(this);
#Override
public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) {
// We take the last son in the scrollview
View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1);
int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));
// if diff is zero, then the bottom has been reached
if (diff == 0) {
// do stuff
//TODO keshav gers
pausePlayer();
videoFullScreenPlayer.setVisibility(View.GONE);
}
}

Categories

Resources