I've been having a lot of issues drawing multiple paths in the same relative layout. What happens is that all my paths are drawn on the same spot they were originally drawn. Instead I want to shrink each drawn path/canvas and have them displayed side by side on the page.
My code to draw the paths looks like
for (int x=0; x < paths.size(); x++){
DrawView dw = new CustomView(this);
dw.path = paths.get(x);
dw.paint = paints.get(x);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if (x == 0){
} else {
params.addRule(RelativeLayout.BELOW, x-1);
}
dw.setId(x);
layout.addView(dw, params);
}
I followed other suggestions for TextViews to add an custom layout parameter to display each TextView below each other but this does not appear to work for dynamically drawn paths.
Note: CustomView is a class that extends View and overwrites the onDraw method to draw out my paths.
EDIT:
If it helps my custom class looks like
public class CustomView extends View {
public Path path;
public Paint paint = new Paint();
public CustomView(Context context){
super(context);
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas){
canvas.drawPath(path, paint);
}
}
Is it necessary for you to use a RelativeLayout? If it is not too binding, consider having a LinearLayout which either replaces the RelativeLayout, or is contained within the RelativeLayout. Then you can add all the CustomViews to this LinearLayout, setting the weight field in LinearLayout.LayoutParams to 1.
Alternatively, consider manually setting each CustomView's height to getHeight()/(paths.size()) instead of RelativeLayout.LayoutParams.WRAP_CONTENT.
Related
I need to start activity with two circular bitmaps in the center, that should instantly animate and move slightly to the side, such that a part of one bitmap is offscreen and not visible. Later on, when the user leaves the activity, it should animate the same way but backwards (move from side to center).
So I've implemented a class that draws these bitmaps in their main location (on the side), and the problem I have right now is that when I am animating those, the offscreen part of the bitmap is never shown, so on my screen I see an ugly moving bitmap that is missing a piece.
This is how the bitmaps are drawn:
public class MyImageView extends ImageView {
private Canvas offscreen;
private static Bitmap pattern;
private static Bitmap logo;
private static int screenWidth, screenHeight; //The real size of the screen
private static int patternDiameter, logoDiameter; //bitmaps' dimensions, both images are circles
private static float topPatternMargin, leftPatternMargin;
private static float topLogoMargin, leftLogoMargin;
public MyImageView (Context context, AttributeSet attrs) {
super(context, attrs);
screenWidth = getSize(context)[0];
screenHeight = getSize(context)[1];
patternDiameter = (int)(screenWidth * (1005.0/1280.0));
logoDiameter = (int)(patternDiameter * (230.0/502.5));
pattern = BitmapFactory.decodeResource(context.getResources(), R.drawable.start_pattern);
pattern = Bitmap.createScaledBitmap(pattern, patternDiameter, patternDiameter, false);
logo = BitmapFactory.decodeResource(context.getResources(), R.drawable.start_logo);
logo = Bitmap.createScaledBitmap(logo, logoDiameter, logoDiameter, false);
leftPatternMargin = (float) ((screenWidth) * (430.0/1280.0) - (patternDiameter/2.0));
topPatternMargin = (float) ((screenHeight - patternDiameter)/2.0);
leftLogoMargin = (float) (leftPatternMargin + patternDiameter/2.0 - logoDiameter/2.0);
topLogoMargin = (float)(topPatternMargin + patternDiameter/2.0 - logoDiameter/2.0);
offscreen = new Canvas(pattern);
}
#Override
protected void onDraw(Canvas canvas) {
if(canvas==offscreen)
super.onDraw(offscreen);
else {
super.draw(offscreen);
canvas.drawBitmap(pattern, leftPatternMargin, topPatternMargin, null);
canvas.drawBitmap(logo, leftLogoMargin, topLogoMargin, null);
}
}
}
So my question is how would I change this code (or maybe some other code) such that, when I make an instance of this class move around, all parts of it are visible to the user?
I found the answer here:
When drawing outside the view clip bounds with Android: how do I prevent underling views from drawing on top of my custom view?
The solution is to add this line to the layout:
android:clipChildren="false"
I'm trying to draw a FrameLayout onto a canvas, but I can't get it to work. Drawing an ImageView works fine, so I'm not sure what I'm missing.
Here's an example of one way I'm trying to draw a FrameLayout:
#Override
protected void onDraw(Canvas canvas) {
Context activity = this.getContext();
FrameLayout.LayoutParams cellParams = new FrameLayout.LayoutParams(100, 100);
ImageView cloud = new ImageView(activity);
cloud.setLayoutParams(cellParams);
cloud.setImageResource(R.drawable.transparent_cloud_large);
FrameLayout gameTile = new FrameLayout(activity);
gameTile.addView(cloud);
gameTile.layout(100, 200, 200, 300);
gameTile.draw(canvas);
}
I'm also trying it without setting LayoutParams for the ImageView, and instead using: cloud.layout(0, 0, 100, 100);, but neither way works.
Here's my code for the ImageView, which does work.
#Override
protected void onDraw(Canvas canvas) {
Context activity = this.getContext();
ImageView image = new ImageView(activity);
image.setImageResource(R.drawable.transparent_cloud_large);
image.layout(100, 100, 200, 200);
image.draw(canvas);
}
Do I need to override the FrameLayout's draw method in some way? Why doesn't it work out of the box?
I see that the javadocs say "The view must have already done a full layout before this function is called." Maybe that's my problem? How do I "do a full layout" on a FrameLayout?
I got it to work. It was missing a call to measure. This is what I'm using in the end:
GameTile gameTile = square.getGameTile();
gameTile.measure((int)tilePixels, (int)tilePixels);
gameTile.layout(0, 0, 0, 0);
canvas.save();
canvas.translate(coor.getX() * tilePixels, coor.getY() * tilePixels);
gameTile.draw(canvas);
canvas.restore();
GameTile is a subclass of FrameLayout. The save, translate, and restore methods are used to position the FrameLayout on the canvas.
I'm trying to create a translucent help overlay to be displayed over my activity's main screen when the user first opens the app. I would like to highlight a button contained in the main layout (and inflated using setContentView), by "cutting out" a section of the overlay which corresponds to the position of the button, and make the cutout transparent.
The overlay is a programmatically-created view (which extends RelativeLayout) which is added to my activity's main FrameLayout, like this:
private void addHelpOverlay(){
HelpOverlay help = new HelpOverlay(this);
help.setBackgroundColor(Color.parseColor("#BB222222"));
mainLayer.addView(help);
}
public class HelpOverlay extends RelativeLayout{
public HelpOverlay(Context context){
super(context);
}
#Override
public void dispatchDraw(Canvas canvas){
canvas.drawColor(Color.parseColor("#BB222222"));
Paint mPaint = new Paint();
mPaint.setColor(0xFFFFFF);
mPaint.setAlpha(0);
mPaint.setAntiAlias(true);
canvas.drawCircle(buttonX, buttonY, 100, mPaint);
super.dispatchDraw(canvas);
}
}
The above code doesn't actually show anything, just the translucent layout with no circle cutout. I assume this is because it's just drawing a transparent circle over top of the translucent layout. I'm really struggling to accomplish this, any suggestions would be appreciated!
Try adding PorterDuff to your paint object . which will make the specific area to transparent
Paint mPaint = new Paint();
mPaint.setColor(0xFFFFFF);
mPaint.setAlpha(0);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.TRANSPARENT);
mPaint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
canvas.drawCircle(buttonX, buttonY, 100, mPaint);
If you get a picth black in the circle area, that must be due to graphic rendering problem , you can enable it using below code right before you declare paint object .
if (android.os.Build.VERSION.SDK_INT >= 11) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
I guess this should fix your issue
I am trying set up a kids app where the can draw on a canvas.
I have a layout which has an ImageView and looks something like this:
The Green background with the letter is the imageview, where the canvas should overlay and allow user to draw on. Also I want to allow the user to change the color stroke by pressing on the color globe.
How can I go on with it?
This answer here might help you with allowing the user the canvas: https://stackoverflow.com/a/7401699/2698582.
To allow the user to draw in different colors, you can use a color picker such as this one: https://code.google.com/p/android-color-picker/, or you can google for a different one.
If you define a new point class and change a bit of this example code, you'll be able to add this color change:
private class ColoredPoint {
public int x, y, color = Color.BLACK;
}
public class DrawView extends View implements OnTouchListener {
List<ColoredPoint> points = new ArrayList<ColoredPoint>();
Paint paint = new Paint();
int currentColor = Color.BLACK;
public DrawView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
paint.setColor(currentColor);
}
#Override
public void onDraw(Canvas canvas) {
for (ColoredPoint point : points) {
paint.setColor(point.color);
canvas.drawCircle(point.x, point.y, 2, paint);
}
}
public boolean onTouch(View view, MotionEvent event) {
ColoredPoint point = new ColoredPoint();
point.color = currentColor;
point.x = event.getX();
point.y = event.getY();
points.add(point);
invalidate();
return true;
}
}
Finally, depending on your selected color picker, this code will vary. Basically, you'll add a touch listener to your color globe to show the color popup. Supposing that the popup has an "OK" button, you'll add a button listener to that button. When it is pressed, change the variable "currentColor" to the selected color.
As mentioned in that example post, you can also implement this using Lines. You might consider using a GestureDetector instead. This tutorial should help explain exactly how the GestureDetector works: http://developer.android.com/training/gestures/detector.html.
Take linearlayout and transfer imageview into linearlayout background color. Add your custom view into this linearlayout, and override ondraw method to that view class.
something like this:
<LinearLayout
background:#drawable/foo">
<com.packagename.customview
android:width="match_parent"
android:height="value">
>
</com.packagename.customview>
</LinearLayout>
create customview class and implement drawing part into it.
For the color picker, just use this link: https://code.google.com/p/android-color-picker/
EDIT:
OK, so I've extended the lenght and the width of the view to a big part of the screen I can assume that the onDraw() method is NOT TAKING the limits of the view, because it goes through all the screen as I can suppose. So the question now would be how to tell the canvas to respect the limits of the view? Any ideas, maybe forcing some parameters?
END OF EDIT
I have implemented some code for drawing a custom view, but the canvas is blank. I am sure it is running, because I have added log traces, and those are showing up just fine.
How do I properly implement drawing for custom views, and what is wrong with what I've done below?
My custom view that goes inside a layout like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/relativeLayout1" android:layout_height="fill_parent"
android:layout_width="fill_parent">
<pis.core.JMI_VistaMenuIn android:layout_height="150dip" android:layout_width="150dip" android:layout_alignParentTop="true" android:id="#+id/viewAnd01" ></pis.core.JMI_VistaMenuIn>
<Button android:layout_width="wrap_content" android:id="#+id/btnJugar" android:layout_height="wrap_content" android:text="Jugar" android:layout_below="#+id/viewAnd01" android:layout_alignLeft="#+id/viewAnd01" android:layout_alignRight="#+id/viewAnd01"></Button>
</RelativeLayout>
The customview is the one called: pis.core.JMI_VistaMenuIn. In the preview I got an "java.lang.UnsupportedOperationException" but when the .apk is loaded the xml layout WORKS.
Here is the code for my custom view. As I said before, the lines that contain the logging are getting hit, but my canvas is blank:
public class JMI_VistaMenuIn extends View {
public static final String QUOTE = "Mind Logic+";
public Animation anim;
//Default constructors
public void createAnim(Canvas canvas) {
Log.i("SC", "Exe canvas 01");
anim = new RotateAnimation(0, 360, canvas.getWidth() / 2, canvas
.getHeight() / 2);
anim.setRepeatMode(Animation.REVERSE);
anim.setRepeatCount(Animation.INFINITE);
anim.setDuration(10000L);
anim.setInterpolator(new AccelerateDecelerateInterpolator());
startAnimation(anim);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// creates the animation the first time
if (anim == null) {
createAnim(canvas);
}
Path circle = new Path();
int centerX = canvas.getWidth() / 2;
int centerY = canvas.getHeight() / 2;
int r = Math.min(centerX, centerY);
circle.addCircle(centerX, centerY, r, Direction.CW);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setTextSize(30);
paint.setAntiAlias(true);
canvas.drawTextOnPath(QUOTE, circle, 0, 30, paint);
Log.i("SC", "Exe canvas 02");
}
}
Similar code works just fine for me, including the animation. You have another issue somewhere else in your code: try setting the paint style.
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setTextSize(30);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);