I have a custom view, which draws a concave shape (visualized with red rectangle on screenshot). This custom view is a part of my recycler view element layout, which also contains a plain view with background color (right part).
This is an extract of my custom view (without rotation, but same drawing methods):
public class InvertedCircleView extends View {
private Paint mPaint;
private float mCanvasCenterX;
private float mCenterCircleWidth, mCenterCircleHeight;
public InvertedCircleView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
[...]
mPaint.setStyle(Paint.Style.FILL);
canvas.drawPaint(mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mCenterCircleWidth = canvas.getWidth();
mCenterCircleHeight = canvas.getHeight();
mCanvasCenterX = canvas.getWidth() / 2;
canvas.drawOval(mCanvasCenterX - (mCenterCircleWidth / 2),
-mCenterCircleHeight,
mCanvasCenterX + (mCenterCircleWidth / 2),
mCenterCircleHeight,
mPaint);
}
}
When the recyclerview shows up the first time, everything looks fine. But when i scroll down (or up), the custom view part is not visible on all the new elements.
What i have tested so far:
setItemViewCacheSize -> this helps, but when i scroll up again, it shows the same bad result
notifyDataSetChanged -> this directly results in the "wrong" visualization for all elements
What may be the reason for this behaviour?
Found my mistake:
I used "mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));" in the onDraw-function. But i forgot to reset Xfermode in the end.
I added the line " mPaint.setXfermode(null);" and everything works as expected :)
Related
I have a library that shows a camera preview. I want to add a rectangle overlay on top of the preview. I tried 2 different approaches. But both of them display the rectangle shortly then disappear.
approach (using view.getOverlay)
mPreview.setZOrderMediaOverlay(true);
mPreview.setZOrderOnTop(true);
ViewGroup rootView = (ViewGroup)mActivity.getWindow().getDecorView().findViewById(android.R.id.content);
rootView.addView(mPreview);
final ViewOverlay overlay = mPreview.getOverlay();
final Bracket bracket = new Bracket();
mPreview.post(new Runnable() {
#Override
public void run() {
bracket.setBounds(0, 0, mPreview.getWidth(), mPreview.getHeight());
overlay.add(bracket);
}
});
approach (overriding draw function in surfaceview)
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.GREEN);
paint.setStrokeWidth(10);
//center
int x0 = canvas.getWidth()/2;
int y0 = canvas.getHeight()/2;
int dx = canvas.getHeight()/3;
int dy = canvas.getHeight()/3;
//draw guide box
canvas.drawRect(x0-dx, y0-dy, x0+dx, y0+dy, paint);
}
mPreview extends SurfaceView
Bracket extends Drawable
UPDATE
If I use setContentView instead of rootView.addView it works as expected. But in this case I can not remove the view.
I used a new Activity and setContentView. That solved the problem.
To remove the view I finished the action.
I am trying to draw a single line in Android using canvas
My class :
public class LineDrawer extends View {
public LineDrawer(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setStrokeWidth(10);
float left = 20;
float top = 20;
float right = 50;
float bottom = 100;
canvas.drawLine(left, top, right, bottom, paint);
}
}
My Main Activity :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LineDrawer lineDrawer = new LineDrawer(this);
setContentView(R.layout.activity_Main);
}
}
I cannot find where is the problem , I try all the solutions in the internet but nothing happen , still a blank activity..
Should I import some code ?
lineDrawer is created but not added anywhere. Just creating a view is not enough, you need to add it to the current displayed views to be taken into account and drawn. You have two options:
Add it to your XML layout. You will have to add the following constructor to your custom view.
public LineDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
}
Use addView(). Anyway, given how simple is your example, I'll use first (common) method.
As an additional comment, the Paint paint object should be created on view initialization, as is a costly operation. See in the original documentation for more information about this.
I'm trying to prevent over-draw in my app, and in one of the root views I'd like to detect the location of my children views, and then draw a background for where they are not. From my understanding, clipping would clip outside of the path, but what I'd really like to do is not draw at certain locations on the screen where the children are. Is there a good way to do this?
EDIT:
So I haven't played with canvas in a while, but I'm looking to do something like this:
public class ContainerView extends FrameLayout {
private final Paint mBackgroundPaint = new Paint();
private final Path mPath = new Path();
public ContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
mBackgroundPaint.setColor(context.getResources().getColor(R.color.default_background));
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPaint(mBackgroundPaint);
mPath.reset();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
mPath.addRect(child.getLeft(), child.getTop(), child.getRight(), child.getBottom(),
Path.Direction.CCW); // What direction do I want?
}
canvas.clipPath(mPath);
}
}
clipPath just wont do what I want though, I don't think. I think that would clip outside the path, and I need to clip inside the path.
You'd probably want to do something like override onLayout and at the end of it:
iterate over all your child views
check if the child is visible/opaque
build up a list of each child's position/size
use that list to do your own background clipping
I would like to add TextView and EditView into my custom LinearLayout programmatically.
But I don't know how.
Something like this (that doesn't work):
<com.custom.FavoritesViewer
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="gone"
android:id="#+id/favoritesViewer">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello"/>
</com.custom.FavoritesViewer>
and my custom layout
public class FavoritesViewer extends LinearLayout {
private Bitmap fullImage;
private int canvasWidth;
private int canvasHeight;
private final Paint paint = new Paint();
public FavoritesViewer(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
initializeCanvasSize(context);
}
private void initializeCanvasSize(Context context) {
final Pair<Integer, Integer> screenSize = Utils.getScreenSize(context);
canvasWidth = screenSize.first;
canvasHeight = screenSize.second;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(canvasWidth, canvasHeight / 3);
}
#Override
protected void onDraw(Canvas cvs) {
if (fullImage == null) {
fullImage = Bitmap.createBitmap(canvasWidth, canvasHeight / 3, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(fullImage);
paint.reset();
paint.setColor(Color.parseColor("#AA000000"));
canvas.drawRect(0, 0, canvasWidth, canvasHeight / 3, paint);
}
cvs.drawBitmap(fullImage, 0, 0, null);
}
}
So I have a canvas (like background) and I would like to add some standart Views on top. I cannot add it on onDraw.
Is it any way to add View into custom Layout?
EDITTED
I need to implement some special UI with buttons. I want to wrap this in one component. I draw that UI on canvas and somehow should add buttons (It's enough for me to add simple ImageButton, not to draw an image and emulate button's behaviour). That's why I selected Layout as container and need to add Views programmatically.
As long as you call through to super (probably super.onDraw()), I imagine the parent class will draw the views you add as expected. It looks like you're just overriding onDraw, which would prevent the parent LinearLayout class from rendering it's content (like the TextView).
Try commenting out your onDraw method first, and see if the LinearLayout behaves as expected.
Also, what's the goal of the Custom Layout? There may be a better way to achieve your goal.
What is the simplest way to draw pixels, lines and circles on a View?
I want to move a cross cursor around, so nothing particularly intensive.
I thought I could extend SurfaceView and add it to an XML and it would just work, but it just appears black, however, when I look at the layout view of localmap.xml in eclipse, the graphics appear as expected.
Any ideas? My onDraw is never called on the emulator, and even calling invalidate on the class makes no difference. I shall keep trying but can anyone see anything I've missed? or is there a better way entirely?
Frink
localmap.xml contains the following (in a RelativeLayout)
<com.example.android.game.LocalMapView
android:id="#+id/localmap_map"
android:layout_width="fill_parent"
android:layout_above="#id/localmap_planettext"
android:layout_below="#id/header"/>
LocalMapView.java contains the following (amongst other things)
public class LocalMapView extends SurfaceView {
Paint mPaint = new Paint();
//Construct a LocalMapView based on inflation from XML
public LocalMapView(Context context, AttributeSet attrs) {
super(context, attrs);
// allow the map to receive the focus
setFocusable(true);
}
private void drawPixel(Canvas canvas, int x, int y, int colour) {
mPaint.setColor(colour);
if ((x >= MAP_MIN_X) && (x < MAP_MAX_X) && (y >= MAP_MIN_Y) && (y < MAP_MAX_Y)) {
canvas.drawPoint(((float)x * mScaleMapToScreenX), ((float)y * mScaleMapToScreenY), mPaint);
}
}
private void drawCircle(Canvas canvas, int x, int y, int radius, int colour) {
mPaint.setColor(colour);
canvas.drawCircle(((float)x), ((float)y), ((float)radius), mPaint);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawCircle(canvas, MAP_MAX_X/2, MAP_MAX_Y/2, 1, 0xFF00FFFF);
drawPixel(canvas, MAP_MAX_X/2, MAP_MAX_Y/2, 0xFF000000);
}
With SurfaceView you don't do the drawing in onDraw(). You have to grab a canvas from the underlying surface and draw in there. It seems to me you don't really know why you are using a SurfaceView. Just use a normal View instead and onDraw() will work just fine.