3

enter image description here

I have made a layout, which has two RelativeLayout which behave as two sliders (like Notification on status bar), which the user can drag up and down, and accordingly I have to change top and bottom text value.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black" >

<RelativeLayout
    android:id="@+id/handle1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white" >

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="40dp"
        android:layout_marginLeft="95dp" >

        <TextView
            android:id="@+id/top_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="Top text"
            android:textColor="@color/black"
            android:textSize="18sp" />
    </FrameLayout>
</RelativeLayout>

<RelativeLayout
    android:id="@+id/handle2"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white" >

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="95dp"
        android:layout_marginTop="40dp" >

        <TextView
            android:id="@+id/bottom_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="Bottom text"
            android:textColor="@color/black"
            android:textSize="18sp" />
    </FrameLayout>
</RelativeLayout>

During layout, the two RelativeLayout lie on top of each other, and I offset them to the shown initial position using offsetTopAndBottom(int offset)

Now, I track MotionEvents on both RelativeLayout, and call this method on ACTION_MOVE event.

private void offsetHandle(View view, int distanceY) {
    switch(view.getId()) {
    case R.id.handle1:
        mTopHandle.offsetTopAndBottom(distanceY);
        mTopSliderPosition = mTopHandle.getBottom();
        break;

    case R.id.handle2:
        mBottomHandle.offsetTopAndBottom(distanceY);
        mBottomSliderPosition = mBottomHandle.getTop();
        break;
    }
}

Here mTopHandle and mBottomHandle are the two white RelativeLayouts.

Note : I don't use LayoutParams and change top and bottom margins respectively here because that calls requestLayout repeatedly and is not smooth at all.

enter image description here

Now, I have to change top text value (which is a child of top RelativeLayout) when the parent RelativeLayout offsets on move event. But, when I call setText, it implicitly calls requestLayout() which resets all offsets done on both handles.

Now, this is a deal breaker since I have come so far in making these sliders. I tried overriding requestLayout() to do nothing on TextView but then text is not set properly a.k.a it shows random behaviour.

Even if I remove both text views out from the RelativeLayouts, then also a call to requestLayout() will force the parent FrameLayout to relayout all its child views.

Any suggestions ?

himanshurb
  • 1,115
  • 1
  • 12
  • 22

3 Answers3

2

I just ran into a similar issue where I was offsetting a View and everytime I made changes to other elements in the surrounding RelativeLayout the offet reset. I was able to fix my problem by setting an OnGlobalLayoutListener to my root RelativeLayout's ViewTreeObserver. Then in its onGlobalLayout() method I reset the correct offset for my View. So everytime the layout redraws itself the correct offset is restored.

For this to work you have to save the total offset of your view everytime it changes. So in your case you need a field in your class (e.g. int mTotalOffset) and then set it to your total offset in offsetHandle():

private void offsetHandle(View view, int distanceY) {
    mTotalOffset = mTotalOffset + distanceY;
    switch(view.getId()) {
    ...
}

Then wherever you inflate your layout, add

rootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {         
    @Override
    public void onGlobalLayout() {
        mHandle1.offsetTopAndBottom(mTotalOffset);
        mHandle2.offsetTopAndBottom(mTotalOffset);
        ...     
    }
});

Hope this helps.

Cheers!

tpauls
  • 21
  • 2
1

Maybe it's a bit late but either way I'll give it a shoot!

Have you tried setting a fix size to the textview? I've also had some trouble with a pretty similar scenario and I found 2 other stackoverflow questions: this one and this one, which talk about this problem (the second one is more detailed and shows source code for the TextView). Apparently the problem is that after calling method setText the view controls whether it has enough space for showing the new text, roughly speaking: if it has a dynamic width, it needs to start the layout process because it needs to know its available space (value given by its parent), but if it has a fixed size (specially the width), it only invalidates itself.

I won't go into too much detail about the drawing process… (not that I understand it too well myself) but that's how I understood it then and it made sense to me. ;)

hope it works for you too!

cheers!

Juan

Community
  • 1
  • 1
Juan
  • 936
  • 8
  • 10
0

I ran into the same issue as you. I only need to toggle between a known set of values, so I was able to make it work by pre-defining all of the text fields in the layout and toggling them on and off with myView.setVisibility(View.INVISIBLE) or myView.setVisibility(View.VISIBLE) which don't call requestLayout(). If you have too many possible values to display to do this, you might try isolating the text view from the layout in such a way that even if it is redrawn, the redraw doesn't cascade to all parents/siblings/etc (fragments?).

  • Agreed, I also thought of that hack, but I dont have discreet values (a.)), also I need to move my TextView with the slider, even if it shifts by 0.1px (b.)), thats why I kept it inside the slider layout only, but then it calls requestLayout (which I have blocked for time being), and screws everything. It's so sad that the platform works in such a way which discourages us to develop such interactions. Thanks for sharing your hack. – himanshurb Jan 13 '14 at 17:24