I have a custom view with id R.id.dragableview inside a RelativeLayout, which moves up and down by drag and drop. My problem is, in the layout there is another view which depends on the dragable one:
<View android:id="#+id/dependent_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/dragableview"
/>
My problem is, when the dragable view moves, the other stays where it originally was. I want it to be below the other view always!
how can I achieve that? Which method should I call from inside my custom view, so the other view can update its position?
thanks
I think you've got two options:
Use a RelativeLayout and position your 2nd view at some position relative to your draggable view.
If option #1 doesn't work, you can also try translating your 2nd view by the same distance the 1st view gets dragged, using setTranslationX (float translationX) and setTranslationY (float translationY).
Related
I want to execute some action if user click the space(created by margin) between two items in RecyclerView, however I did not found a way to do that. OnTouchListener might do the trick but it is posible to get view by given coordinate (x, y)?
Setting onTouchListener on root does not do the trick, it seems like the event is handled by view on top of it. I want to register that can handle all ontouch event and pass the ontouch event to the right view after pre-handle.
You can also achieve this by creating margin using View tag-
Something like this-
<View
android:id="#+id/left_margin_view"
android:layout_width="10dp"
android:layout_height="match_parent"/>
Remove margin applied to recycle item root tag and create view and now you can use any event listener with this view id left_margin_view.
I'm wrote a custom Android View that need to draw outside its clipping Bounds.
This is what I have:
This is what happens when I click a button, say the right button:
How do I prevent the View below to draw on top of my "handle"?
Some related pseudo-code from my project follow.
My custom view MyHandleView draw like this:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Path p = mPath;
int handleWidth = mHandleWidth;
int handleHeight = mHandleHeight;
int left = (getWidth() >> 1) - handleWidth;
canvas.save();
// allow drawing out of bounds vertically
Rect clipBounds = canvas.getClipBounds();
clipBounds.inset(0, -handleHeight);
canvas.clipRect(clipBounds, Region.Op.REPLACE);
// translate up to draw the handle
canvas.translate(left, -handleHeight);
// draw my background shape
canvas.drawPath(p, mPaint);
canvas.restore();
}
The layout is something like this (I simplified a little):
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Main content of the SlidingUpPanel -->
<fragment
android:above=#+id/panel"
class="com.neosperience.projects.percassi.android.home.HomeFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?android:attr/actionBarSize"
tools:layout="#layout/fragment_home" />
<!-- The Sliding Panel -->
<LinearLayout
android:id="#id/panel"
android:layout_width="match_parent"
android:layout_height="#dimen/myFixedSize"
android:alignParentBottom="true"
android:orientation="vertical"
android:clipChildren="false">
<MyHandleView xmlns:custom="http://schemas.android.com/apk/res-auto.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp"
custom:handleHeight="#dimen/card_panel_handle_height"
custom:handleWidthRatio="#dimen/card_panel_handle_width_ratio"
custom:handleBackgroundColor="#000"/>
<TextView
android:id="#+id/loyaltyCardPanelTitle"
android:layout_width="match_parent"
android:layout_height="#dimen/card_panel_height"
android:background="#000"
android:gravity="center"
android:textStyle="bold"
android:textSize="24sp"
android:textColor="#fff"
android:text="My TEXT"/>
</LinearLayout>
</RelativeLayout>
You can think of the fragment as a view containing two button at the bottom, in a LinearLayout.
In place of the external RelativeLayout I'm really using a view from a library: SlidingUpPanelLayout (https://github.com/umano/AndroidSlidingUpPanel). But I tested the behavior with this RelativeLayout: same thing, meaning the library is not related.
I say this just to let you know that I can't just place there a FrameLayout and avoid drawing outside the clipping bounds.
I suspect this has something to do with the fact that when I touch the button it redraw itself but my other view (which is somewhere else in the hierarchy) doesn't get re-drawed (this is an optimization and I doubt it can be disabled).
I'd like to be able to invalidate my custom view whenever any other "near" (or any) view get's invalidated, thus I need some kind of listener on view invalidation but I'm not aware of any.
Can someone help?
I found the solution myself, even if this is not optimal for performances.
Just add:
android:clipChildren="false"
to the RelativeLayout (or whatever layout you have).
This has 2 effects (may be more, this are the two that interested me):
- the ViewGroup doesn't clip the drawing of his children (obvious)
- the ViewGroup doesn't check for intersection with dirty regions (invalidated) when considering which children to redraw
I digged the View code about invalidating.
The process goes, more or like, like this:
a View invalidate itself, the region it usually draw (a rectangular) become a "dirty region" to be redrawed
the View tell its parent (a ViewGroup of some kind) it need to redraw itself
the parents do the same with it's parent to the root
each parent in the hierarchy loop for every children and check if the dirty region intersect some of them
if it does it also redraw them
In step 4 clipping is involved: the ViewGroup check view bounds of his child only if clipChildren is true: meaning that if you place it to false it always redraw all its children when any of them is invalidated.
So, my View hierarchy was like this:
ViewGroup A
|
|----- fragment subtree (containing buttons, map,
| whatever element that I don't want to draw
| on top of my handle)
|
|----- ViewGroup B
|
|---- my handle (which draw outside its clip bounds)
In my case the "handle" draw ouf of it's bound, on top of something that is usually drawed by some element of the fragment subtree.
When any view inside the fragment is invalidated it pass its "dirty region" up in the view tree and each view group check if there are some other children to be redraw in that region.
ViewGroup B would clip what I draw outside the clip bounds if I do not set clipBounds="false" on it.
If anything get's invalidated in the fragment subtree the ViewGroup A will see that ViewGroup B dirty region is not intersecting the fragment subtree region and will skip redrawing of ViewGroup B.
But if I also tell ViewGroup A to not clip children it will still give ViewGroup B an invalidate command which will then cause a redraw of my handle.
So the solution is to make sure to set
android:clipChildren="false"
on any ViewGroup in the hierarchy above the View that draw out of it's bounds on which the content may fall "under" the out-of-bound region you are drawing.
The obvious side effect of this is that whenever I invalidate any of the view inside ViewGroup A an invalidate call will be forwarded, with the invalid region, to all the view in it.
However any view that doesn't intersect the dirty region which is inside a ViewGroup with clipChildren="true" (default) will be skipped.
So to avoid performance issues when doing this make sure your view groups with clipChildren="true" have not many "standard" direct children. And with "standard" I mean that they do not draw outside their view bounds.
So for example if in my example ViewGroup B contains many view consider wrapping all those in a ViewGroup with clipChildren="true" and only leave this view group and the one view that draw outside its region as direct children of ViewGroup B. The same goes for ViewGroup A.
This simple fact will make sure no other View will get a redraw if they aren't in the invalidated dirty region minimizing the redraws needed.
I'm still open to hear any more consideration if someone has one ;)
So I'll wait a little bit before marking this as accepted answer.
EDIT: Many devices do something different in handling clipChildren="false". I discovered that I had to set clipChildren="false" on all the parent views of my custom widget that may contains elements in their hierarchy which should draw over of the "out of bound region" of the widget or you may see your custom drawing showing ON TOP of another view that was supposed to cover it. For example in my layout I had a Navigation Drawer that was supposed to cover my "handle". If I didn't set clipChildren="false" on the NavigationDrawer layout I may sometimes see my handle pop up in front of the opened drawer.
EDIT2: My custom widget had 0 height and drawed "on top" of itself. Worked fine on Nexus devices but many of the others had some "optimization" in place that completely skip drawing of views that have 0 height or 0 width. So be aware of this if you want to write a component that draw out of it's bound: you have to assign it at least 1 pixel height / width.
I have a horizontal scroll view, I also have a relativelayout as it's child. I am adding child views of this relativelayout dynamically. I have a header text which should be update when I scroll according to respective child views. How can I do this because I am able to get the current focused item in horizontal scroll. Please give me some suggestion or examples which can be helpful for me, thanks..
You should specify an OnTouchListener for your HorizontalScrollView and in it's onTouch() method detect the type of MotionEvent and change your TextView's color to the appropriate
If you are creating this childs dinamically you can set a tag to them with the content you want to show in the header TextView.
//Creating RelativeLayout childs
TextView newChild = new TextView(this).
newChild.setTag(textToShowWhenThisItemIsFocused);
Then if you know which item is focused you just have to get the tag.
// "selected" is the focused view
header.setText((String) selected.getTag());
When to use the second code depends on your implementation. Since you didn't provide any code it's hard to know how to monitor the scroll, but i.e. you could control Touch Events and update the header when the user is moving his finger over the screen (you should also take into account the inertia after the user stops tapping).
EDIT: How to get the focused View
First of all, I barely have experience doing things like this, so I'm not sure if this is going to work or if there are better ways to do it. I'll just tell you the way I would approach this problem.
To be able to know the focused View you need to know the coordinates where this View should be. Since it's an horizontal ScrollView we will need the X coordinate. Since we want the View in the middle of the ScrollView I would do it like this:
private int centerSV;
private ScrollView mScrollView;
...
centerSV = mScrollView.getWidth()/2;
Now we have the center of the ScrollView. Now we need to know which child is in this position:
private int getFocusedChildId(){
for(int i=0; i<mChilds.length; i++){
int childLeftCoord = mChilds[i].getLeft() - mScrollView.getScrollX();
if(childLeftCoord <= centerSV && centerSV <= childLeftCoord + mChilds[i].getWidth())
return mChilds[i].getId();
}
// No view found in the center, maybe ScrollView wasn't full. Return the first one
return mChilds[0].getId();
}
Again, I'm not sure if this is going to work, it's just an idea of how to approach your issue. Also, you should take this into account:
getWidth() and getHeight() of View returns 0
I want to place a View (A) on top of another View (B). However, when I do so using a FrameLayout or a RelativeLayout (such that View A and View B are both set to be in the same position) I can see View B faintly through View A. I want instead for View A to obscure View B totally out of sight. Is this is possible?
Note: ViewSwitcher will not do for my particular need nor will setting the visibility of View B to 'invisible'/'gone' as I need View B to be
'visible' and fixed in place whilst View A is sliding/animating in and
out of sight.
Yes, that is possible. You can set the visibility of the corresponding View to either View.INVISIBLE or View.GONE. Do this in code with setVisibility(int) or use xml.
<View ... android:visibility="gone" />
Did you try declaring (View) B before declaring (View) A?
Can you show me your xml layout, might be able to help.
It is possible by giving View A a background resource, i.e. an opaque drawable or color.
i use this a s surface view in my application and now i want to add text view to this programmatically. How can i do that.
"<com.csfcse.udrawer.UdrawerDrawingSurface
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/drawingSurface"
android:layout_gravity="left" />"
Thanks......
An option may be to have a FrameLayout with two children, first the SurfaceView, then whatever you want to overlay on the SurfaceView. In the sample below, the second View in the FrameLayout is a horozontil LinearLayout with a Button and a TextView. The FrameLayout displays all its children as piled in Z order with the first child at the bottom and all children positioned at the upper left corner. In this case the LinearLayout has Gravity.CENTER_VERTICAL (I think you could do the same thing with padding on the LinearLayout.
I have never found much (good) documentation about how SurfaceViews (or any of the screen drawing) works. There may be a problem with flickering or refresh, but I don't see much problem on my Froyo Evo. (This test app draws 'twirling' lines on the SurfaceView below the Button and TextView.
If the question is simply: How do you programmatically add a TextView to a Layout that was created by a XML inflated Layout, then, get a reference to the Layout instance and call addView() to it.
Layout lay = parentLayo.findViewById(R.id.drawingSurfaceParent);
TextView tv = new TextView(this);
tv.setText("New textview");
lay.addView(tv);
You'll need to use the Canvas and a corresponding text draw method:
drawText(String text, int index, int count, float x, float y, Paint paint).
This is not possible, a SurfaceView cannot contain other Views.