I have a custom LinearLayout class which I'm using to clip all of its child Views through Canvas#clipPath():
public class MaskedLinearLayout extends LinearLayout {
protected Path mMaskingClip = new Path();
(...)
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// Update clipping path
mMaskingClip.reset();
(...)
mMaskingClip.close();
}
#Override
public void dispatchDraw(Canvas canvas) {
int save = canvas.save();
canvas.clipPath(mMaskingClip);
super.dispatchDraw(canvas);
canvas.restoreToCount(save);
}
}
The code above works great, but the background Drawable that I've set for the same MaskedLinearLayout is not clipped to the path that I've defined (only its children).
How should I accomplish this? Some things that I've tried include overriding method onDraw() (with similar code shown in method dispatchDraw() above) and setting its background Drawable boundaries manually (Drawable#setBounds()), both without success.
Note that I cannot use static Drawables in this layout, because the latter should be able to be resized/masked dynamically while being displayed on the screen.
Thank you in advance for any answer.
Related
I have an Android application that create all the layout programmatically in the onCreate method, after that I need to do some calculation on the coordinates of some of the views I created, but when I try to get coordinates on screen, it always return 0,0.
I think that may be caused from the fact that the activity is still not rendered as in fact by debugging I still see a grey screen if i place a breakpoint on the point where I get coordinates, how can I calculate coordinates after starting activity?
For me it's ok even if it's out of onCreate, I just need it to be executed after that the activity rendering, and if possible I'd like to not use a OnGlobalLayoutListener (if there's no other way well... i'll just get used to the idea, but i'd like to know if there's some other way).
Ah, I even tried to put it on the onResume, but it doesn't work, probably when onResume is called the rendering is still not finished.
Extends View class and writing your own protected void onSizeChanged(int w, int h, int oldw, int oldh) { could help you.
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
final float yCenterX = w / 2;
final float yCenterY = h / 2;
...
}
I have a horizontal scrollview (which has a single view group inside, obviously) which I'm adding some views into when my activity is created. I want a view to start and end at the middle so I need to give some paddings to right and left respectively to the first and last view. Since the sizes are not measured at onCreate, I calculated them on onSizeChanged but the sizes of views are not calculated yet so no prevail there.
public void add(ArrayList<String> list) {
for(String string : list) {
View view = inflater.inflate(R.layout.textview_with_indicator, null);
views.add(view);
((TextView)view.findViewById(R.id.text)).setText(string);
content.addView(view);
}
invalidate();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(paddingSet && null != views && views.size() > 0) {
int paddingLeft = getWidth() - (views.get(0).getWidth()/2);
int paddingRight = getWidth() - (views.get(views.size()-1).getWidth()/2);
setPadding(paddingLeft, getPaddingTop(), paddingRight, getPaddingBottom());
paddingSet = true;
}
}
I think I need to observe the size change of views instead of whole content but I have no idea how to implement it. I tried to implement onGlobalFocusChanged to scrollview but could not make it work. Any ideas would be appreciated.
I work with an SDK that provides a rectangular glsurfaceview through a callback.
I want to be able to render this view in a circular layout. (i.e.) I would like to display the view on a circular view
I have tried using masking layout such as using a maskable layout https://github.com/christophesmet/android_maskable_layout (works great for images, not so much for videos)
How do I go about clipping and rendering this as a circle?
(The background is constantly changing, so i cant overlay a transparent view on top of this video view. The aim is to have a rectangular glsurface view on top of which there is a circular glsurface view)
**UI Objective : **
I have pretty much the same requirement with a project right now, which involves masking a GLSurfaceView provided by PexKit. The solution that works for me is making a subclass of FrameLayout (or any other ViewGroup) and placing the GLSurfaceView inside it.
Then in the FrameLayout subclass use canvas.clipPath:
private Path clippingPath;
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != oldw || h != oldh) {
int radius = Math.min(w, h)/2;
clippingPath = new Path();
clippingPath.addCircle(w/2, h/2, radius, Path.Direction.CW);
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
int count = canvas.save();
canvas.clipPath(clippingPath);
super.dispatchDraw(canvas);
canvas.restoreToCount(count);
}
In Android I'm trying to make a Custom View where the user can sign, like the android finger paint api demo, and it has an overlay (essentially a TextView) that goes away when touched. I need to be able to add this view programmatically.
I couldn't figure out how to make this in an xml layout, so I added the overlay view in onSizeChanged.
It works but sometimes when I rotate the device twice rapidly it doesn't display even though it should. The rest of the View works fine. My View is called PaintOverlayView.
The relevant code is
private TextView overlay= new TextView(getContext());
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
...
LayoutParams wrapped = new LayoutParams(w,h);
overlay.setId(50);
overlay.setText("Click to draw, double click to clear");
overlay.setTextSize(20);
overlay.setGravity(Gravity.CENTER);
overlay.setX(this.getX());
overlay.setY(this.getY());
overlay.setLayoutParams(wrapped);
overlay.setOnTouchListener(new OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("Touch Event");
v.setVisibility(View.GONE);
overlayed=false;
//System.out.println("overlayed on tocuh overlay "+overlayed);
return false;
}
});
ViewGroup Par =(ViewGroup)this.getParent();
Par.addView(overlay);
System.out.println("overlayed on Size Changed "+overlayed);
System.out.println("visibility "+overlay.getVisibility());
/*if (!overlayed)
overlay.setVisibility(View.GONE);
else
overlay.setVisibility(View.VISIBLE);
*/
}
I rotate it and sometimes the overlay doesn't show up even though ovarlyed is true and the visibility is still set to 0 (visible).
Edit: In case it helps I'm trying to add the PaintOverlayView to a RelativeLayout
#Dan S, gave the below statement in this post -> display a still image map
Then using a second ImageView (for the indicator) draw it on top and
move it to the correct place on the screen and make sure the
background in this View is transparent as well as the background of
your indicator
How can I create the second ImageView for my indicator? It should be transparent imageview as well as the indicator image.
UPDATE:
I am asking here on how to overlap my image map ImageView and transparent indicator ImageView in order to mark the current location.
An ImageView is transparent by default.
Your indicator image resource should have a transparent background (e.g. transparent .PNG image).
Just set the ImageView's imageResource or backgroundResource to the PNG file.
So your code for creating your ImageView will be something like this:
ImageView myImageView = new ImageView(context);
myImageView.setBackgroundColor(0x0); // not needed - it's the default
myImageView.setImageResource(R.drawable.indicator_icon);
but again, this's the default and you still have to make sure your image's background is transparent otherwise the ImageView's background won't matter.
UPDATE: following #eros update of the question, here's an updated answer:
There are two options, i can think of, to achieve positioning of one imageview on top of the other:
use the LayoutParams and set the margins to the position the indicator imageview
draw the indicator imageview on top of the map bmp's canvas
personally i like the first option better because future changes won't force you to repaint the indicator.
here's a class demonstrating option (1):
public class MyMapView extends RelativeLayout {
private ImageView mBackImageView;
private ImageView mIndicatorImageView;
public MyMapView(Context context) {
super(context);
mBackImageView = new ImageView(context);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
mBackImageView.setImageResource(R.drawable.image1);
mBackImageView.setScaleType(ImageView.ScaleType.FIT_XY);
addView(mBackImageView, params);
mIndicatorImageView = new ImageView(context);
RelativeLayout.LayoutParams indParams = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
mIndicatorImageView.setImageResource(R.drawable.image2);
addView(mIndicatorImageView, indParams);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
centerIndicatorPosition();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerIndicatorPosition();
}
// #eros: this's the part you want. this method just centers the indicator view
// but if you have the relative position like you say, use it for the margins
private void centerIndicatorPosition() {
int xPos = (getMeasuredWidth() - mIndicatorImageView.getMeasuredWidth()) / 2;
int yPos = (getMeasuredHeight() - mIndicatorImageView.getMeasuredHeight()) / 2;
((RelativeLayout.LayoutParams)mIndicatorImageView.getLayoutParams())
.setMargins(xPos, yPos, 0, 0);
}
}