I have home activity on which I have 3 buttons : Move To A , Move To B and Move To C.
On click of these buttons, I navigate to inner activity where I have 3 layouts within a HorizontalScrollView, say A, B and C which in turn consists of several views.
Now what I want is , inner activity should navigate to one of the 3 layouts based on clicked button from Home activity i.e.
If Move To A on home activity is pressed, inner activity should navigate to Layout A and so on...
Possible solution might be to use ScrollTo() but it isn't efficient with variable screen sizes.
Any help appreciated.
Edit
This doesn't work:
RelativeLayout LayoutA = (RelativeLayout) findViewById(R.id.LayoutA);
HorizontalScrollView main = (HorizontalScrollView) findViewById(R.id.scrollview);
main.scrollTo(PoCLayout.getLeft(), 0);
Even this doesn't work:
main.scrollTo(500, 0);
If main.scrollTo(500,0) does not work, try :
main.post(new Runnable() {
#Override
public void run() {
main.scrollTo(500, 0);
}
}
Edit : the working solution in the listener of the button :
main.post(new Runnable() {
#Override
public void run() {
main.scrollTo(layoutX.getLeft(), 0);
}
}
I'm not sure it will work, but you can try to get the position of your layout A, B or C calling the method getLeft() and then use scrollTo().
Example to scroll to layout A : scrollTo(a.getLeft(), 0);
You can do a little Math here
Use View.getLeft() and View.getRight() then divide calculated width per 2 to scroll to the View center
Use :
final HorizontalScrollView scrollView= (HorizontalScrollView)
int speed=1000;
findViewById(R.id.main_scrollView);
final View view = findViewById(R.id.your_view);
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() { view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
ObjectAnimator.ofInt(scrollView, "scrollY", (int) view.getX()).setDuration(speed).start();
}
});
main.post(new Runnable() {
#Override
public void run() {
main.scrollTo(500,500);
}
}
Related
I want to get width and height of my layout after it had been drawn.
My code is actually called in the createView() method. But I want to wait until the layout is drawn before execute this code :
Log.i(TAG, "Height: "+myButton.getHeight()+" - width : "+myButton.getWidth());
// result is Height: 0 - width : 0
Is there any event launched after onCreateView() call ?
Using a global layout listener has always worked well for me. It has the advantage of being able to remeasure things if the layout is changed, e.g. if something is set to View.GONE or child views are added/removed.
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// inflate your main layout here (use RelativeLayout or whatever your root ViewGroup type is
LinearLayout mainLayout = (LinearLayout ) this.getLayoutInflater().inflate(R.layout.main, null);
// set a global layout listener which will be called when the layout pass is completed and the view is drawn
mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
//Remove the listener before proceeding
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mainLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
mainLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
// measure your views here
}
}
);
setContentView(mainLayout);
Depending on your usage, you might want to remove the listener when you're done with it as shown.
createView does not exits. I suppose you mean Fragment.onCreateView
inside onCreateView,
onCreateView(...) {
View view = infalter.inflate...
final View button = view.findViewById
view.post(new Runnable() {
#Override
public void run() {
Log.i(TAG, "Height: "+button.getHeight()+" - width : "+button.getWidth());
}
});
return view;
}
I think onWindowFocusChanged(boolean hasFocus) is what you are looking for.
Keep in mind that it is called whenever the window focus changes.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Log.i(TAG, "Height: "+myButton.getHeight()+" - width : "+myButton.getWidth());
}
I have scrollview with child LinearLayout . I am adding data programmaticaly to it. When i add some data to top of linearlayout it automatically scrolls to top element. But i want something like , user reaches top -> scrolls upside to load previous data ->add data to linearlayout top but should not get focus, after addition complete , if user scrolls then and then only it should display .
How to achieve this?
Well I thought of a way and it works almost perfectly.
I have a LinearLayout (llCommunicationsLayout) inside a ScrollView (svCommunications) .
I inflate a new LinearLayout, I'm going to add views to the top of this new LinearLayout and then add this new layout to the LinearLayout inside the ScrollView.
This is the new layout:
final LinearLayout wrapperLayout = (LinearLayout) mInflater.inflate(R.layout.empty_layout, null);
I add my views to the 0'th position of this layout.
wrapperLayout.addView(view, 0);
After all the views are added into the wrapperLayout, I add the wrapperLayout into the llCommunicationsLayout (the one inside my ScrollView)
llCommunicationsLayout.addView(wrapperLayout, 0);
After this, I calculate their heights after the wrapperLayout is on screen (has a measurable height)
wrapperLayout.post(new Runnable() {
#Override
public void run() {
int wrapperHeight = wrapperLayout.getHeight();
int svHeight = svCommunications.getHeight();
int scrollHeight = Math.abs(svHeight - wrapperHeight);
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int displayHeight = displaymetrics.heightPixels / 4;
svCommunications.scrollTo(0, (scrollHeight + displayHeight));
}
});
Here, I get both the newly added layout's and the ScrollView's heights.
I calculate their difference, add the 1/4 of the height of the screen of my device and scroll it, voila!
It's not perfect, but after the layouts are added, it no longer scrolls to the top of the screen. Experiment with the displayHeight for different results.
Hope this helps someone out.
You can grab the current view which is on top of your LinearLayout then add new content to your LinearLayout and then scroll back to view which was previously on top. The code would be something like:
public void addViewsOnTop(List<View> views) {
final View currentViewOnTop = (linearLayout.getChildCount() > 0) ? linearLayout.getChildAt(0) : null;
// Add Views. Note that views will appear in reverse order
for(View view : views) {
linearLayout.addView(view, 0);
}
// Scroll back to view which was on top
if(currentViewOnTop != null) {
new Handler().post(new Runnable() {
#Override
public void run() {
scrollView.scrollTo(0, currentViewOnTop.getBottom());
}
});
}
}
Solved....
Try this, worked for me ,
lv_chat.setAdapter(adapter);
lv_chat.setSelection(somePreviousPosition);
I'm building an app with 2 fragments (with ViewPager). Each fragment consists of 4 buttons. I use also onTouch listener with these buttons. I use getLocationOnScreen(...) method to get the view's (button) position on screen. But as I slide from first fragment to the second one, the location of the next 4 buttons is relative to the first fragment. I mean, the x coordinate of these buttons is taking into consideration the previous fragment width.
My question is - how do I get the second fragment's buttons position relative to this fragment page and not the previous one?
Thanks!
EDIT : Added a code snippet:
Here is how i keep my buttons position on screen into a hash-map collection (this is done for each of the two fragments i have):
public void getViewsPosition(final ArrayList<View> viewArr) {
for (final View v : viewArr) {
final ViewTreeObserver vto = v.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int[] i = new int[2];
v.getLocationOnScreen(i);
_rect = new Rect(i[0], i[1], i[0] + v.getWidth(), i[1]
+ v.getHeight());
view_to_rect.put(v, _rect);
if (Build.VERSION.SDK_INT < 16) {
vto.removeGlobalOnLayoutListener(this);
} else {
vto.removeOnGlobalLayoutListener(this);
}
}
});
}
}
This keeps the correct coordinates for the first fragment's buttons. But for the second fragment, the x-coordinate is relative to the first fragment. So I get the button's x coordinate within this fragment PLUS the first fragment width. The y-coordinate is, of course, correct within all fragment.
how can I set scroll of scrollview to x pixels, before it's even shown?
In ScrollView I have some Views and i know that it will fill screen. Can I scroll to let say second View, so that first View is not visible when activity is started?
Now I have sth like this, but I think it's not perfect, sometimes, I see first View, and then it's scrolled to the second one
#Override
public void onGlobalLayout() {
if (mHorizontalScrollView.getChildCount() > 0 && mHorizontalScrollView.getChildAt(0).getWidth() > mScreenWidth) {
hsv.scrollTo(100, 0);
}
}
EDIT!!
Second attempt was to use http://developer.android.com/reference/android/view/ViewTreeObserver.OnPreDrawListener.html instead of http://developer.android.com/reference/android/view/ViewTreeObserver.OnGlobalLayoutListener.html
In OnPreDrawListener we can read that At this point, all views in the tree have been measured and given a frame. Clients can use this to adjust their scroll bounds or even to request a new layout before drawing occurs. so basically at this point we should adjust scrolling. So I created:
#Override
public boolean onPreDraw() {
if (hsv.getChildCount() > 0 && hsv.getChildAt(0).getWidth() > mScreenWidth) {
hsv.scrollTo(100, 0);
return false;
}
return true;
}
but now it's never working for me.
I combined onGlobalLayout with code below. It's not so great to use that hack but it still quite efficient. When I get onGlobalLayout event I check if layouts are done and then if it's ok I use method below.
// hsv - horizontal scroll view
private void forceShowColumn(final int column) {
int scrollTo = childViewWidth * column;
if (scrollTo == hsv.getScrollX()) {
return;
}
hsv.scrollTo(scrollTo, 0);
hsv.postDelayed(new Runnable() {
#Override
public void run() {
forceShowColumn(column);
}
}, 10);
}
try View.post(Runnable) in onCreate-method.
I solved this issue by implementing View.OnLayoutChangeListener in the fragment that controls my ScrollView.
public class YourFragment extends Fragment implements View.OnLayoutChangeListener{
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.your_fragment_layout, container, false);
//set scrollview layout change listener to be handled in this fragment
sv = (ScrollView) view.findViewById(R.id.your_scroll_view);
sv.addOnLayoutChangeListener(this);
return view;
}
...
public void onLayoutChange (View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom){
switch (v.getId())
{
case R.id.your_scroll_view:
{
sv.scrollTo(0, sv.getTop());
}
}
}
The correct way to do this, is to call scrollTo() on the ScrollView after the view has been through its measurement and layout passes, otherwise the width of the content in the scrollView will be 0, and thus scrollTo() will not scroll to the right position. Usually after the constructor has been called and after the view has been added to its parent.
See http://developer.android.com/guide/topics/ui/how-android-draws.html for more insight on how android draws views.
MrJre gives the most elegant solution: "override onLayout() in the scrollview and do it there after super.onLayout()"
Example code here:
https://stackoverflow.com/a/10209457/1310343
Use :
scrollTo (int x, int y)
It sets the scrolled position of your view. This will cause a call to onScrollChanged
For more details : see this
ScrollView ScrollTo
I have a ViewFlipper implementation that needs to be improved. This ViewFlipper has three child views. Basically, I want an indicator on which child view is currently active. My ViewFlipper is just a part of a complex layout which also has list views, etc.
Switching of views is also automatic and done in a specified interval.
From Android's SDK reference, I haven't seen any listener for when the ViewFlipper changes the child view.
Do you guys know of a way I can have a listener for that event?
Or are there alternative ways I can implement this feature besides using ViewFlipper ?
Thanks!
If you apply animation (out or in animation) on view switching, you can set listener to an animation and, for example, act on animation end.
viewFlipper.getInAnimation().setAnimationListener(new Animation.AnimationListener() {
public void onAnimationStart(Animation animation) {}
public void onAnimationRepeat(Animation animation) {}
public void onAnimationEnd(Animation animation) {}
});
I find one way to detect which child is actived :
addOnLayoutChangeListener to ViewFlipper, and getCurrentView of ViewFlipper, then compare with childs of ViewFlipper.
remember to removeOnLayoutChangeListener when activity onDestory
private View page1, page2, page3, page4;
private ViewFlipper viewFlipper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.flipper);
page1 = findViewById(R.id.MyFlipper_page01);
page2 = findViewById(R.id.MyFlipper_page02);
page3 = findViewById(R.id.MyFlipper_page03);
page4 = findViewById(R.id.MyFlipper_page04);
viewFlipper = (ViewFlipper) findViewById(R.id.MyFlipper_flipper);
viewFlipper.addOnLayoutChangeListener(onLayoutChangeListener_viewFlipper);
}
View.OnLayoutChangeListener onLayoutChangeListener_viewFlipper = new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if(viewFlipper.getCurrentView() == page1)
Log.d("test", "change to flipper_page1");
else if(viewFlipper.getCurrentView() == page2)
Log.d("test", "change to flipper_page2");
else if(viewFlipper.getCurrentView() == page3)
Log.d("test", "change to flipper_page3");
else if(viewFlipper.getCurrentView() == page4)
Log.d("test", "change to flipper_page4");
}
};
#Override
protected void onDestroy() {
super.onDestroy();
viewFlipper.removeOnLayoutChangeListener(onLayoutChangeListener_viewFlipper);
}
I have created an extended ViewFlipper which does exactly that: DecentViewFlipper
While this is an old question I found a decent approach that works.
public class MainLaunch extends Activity {
... main setup and code
int currentIndex = 0;
int maxIndex = 3;
// set specific animations for the view flipper on showNext
// only showNext while in valid index
public void showNext() {
if( currentIndex < maxIndex )
{
currentIndex++;
viewFlipper.setInAnimation(getBaseContext(), R.anim.slide_in_left);
viewFlipper.setOutAnimation(getBaseContext(), R.anim.slide_out_right);
viewFlipper.showNext();
}
}
// set specific animations for the view flipper on showPrevious
// only showPrevious while in valid index
public void showPrevious() {
if( currentIndex > 0 )
{
currentIndex--;
viewFlipper.setInAnimation(getBaseContext(), R.anim.slide_in_right);
viewFlipper.setOutAnimation(getBaseContext(), R.anim.slide_out_left);
viewFlipper.showPrevious();
}
}
// get current flipped view
public View getCurrentView() {
return viewFlipper.getChildAt(currentIndex);
}
}
Then to use the ViewFlipper you call showNext() or showPrevious anywhere and can get the currently active view by calling getCurrentView(). This helps in setting different animations for left and right flipping and for easily getting current working views.
I know this question already has an answer. But here's an alternative which is a method ViewFlipper inherited from ViewGroup and which seems to be the best from my experience.
Code Snippets Below:
ViewFlipper viewFlipper = findViewById (R.id.myViewFlipperId);
viewFlipper.setOnHierarchyChangeListener(ViewGroup.OnHierarchyChangeListener);
The onChildViewAdded(View, View) method in the callback listener will be invoked whenever a child view is added to the ViewGroup.
Which you can use to detect whenever the ViewFlipper flips.
See ViewGroup.html#setOnHierarchyChangeListener API Docs